Czy jest możliwość wyciagnięcia wartości z metody void?

1

Witam,
bawię się ostatnio z API XTB mam problem. Jest tam Klasa do pobierania cen instrumentów w czasie rzeczywistm (np. EURUSD). jednak używa do tego metody void, czyli wyświtli mi cenę sysoutem jednak nie mogę na niej operować. API wymusza na mnie typ metody. Słyszałem że w c# używa się do takich rzeczy tzw delegatów. Jak to wygląda w Javie i czy w ogóle jest to możliwe. Poniżej moja implementacja metody( nie wiem jak wydobyć zmienną ask żeby przeprowadzać operację:

public class StreamingListener {
    @Override
    public void receiveTickRecord(STickRecord tickRecord) {
        Double ask;
        if (tickRecord.getLevel() == 0) {
            ask = tickRecord.getAsk();
            System.out.println("Ask -> " + ask);
        }
    }
}
1

Możesz zdefiniować jako pole klasy StreamingListener dowolną kolekcję (np. BlockingQueue) i umieszczać 'ask' w tej kolekcji. Z innego wątku możesz konsumować elementy w miarę jak są umieszczane w kolekcji.

2

Nie jesteś w stanie synchronicznie dowiedzieć się jaka jest wartość, możesz ją zapisać i odczytać w innym momencie. To dobry design.

1

W temacie dobrego designu to Future może tutaj pasować

0

Kiedyś dokopałem się do "uchwytu" dla System.out.print i moje natywne funkcje działały na nim (podmieniałem na swój) ale przy powrocie (uchwyt podmieniałem na zapamiętany stary) już nic się nie wyświetlało w konsoli. Inaczej jest to rozwiązane w JDK a inaczej GCCJ, temat nie był palący więc odpuściłem sobie inaczej mój przypadek rozwiązując.

Temat zakładałem tu kilka lat temu jako niezalogowany, odpowiedzi nie było.

1

Możesz przecież w tej metodzie uzyć jakiegoś AtomicReference albo w ogóle dowolnego wrappera typu Future żeby wyciągać wartości z tej funkcji.

0

Możesz tez potraktować to jak strumień i użyć RxJavy/Reactora. Te biblioteki umożliwiają stworzenie strumienia w taki sposób, że cześć kliencka będzie widziała te wartości jako Strumień<String>, na który może się zasubskrybować

https://github.com/ReactiveX/RxJava/wiki/Creating-Observables#create

1

ale zaraz, XTB wystawia web api to jak tam jest klasa, która wystawia void wypisująca na konsole? a potem podajesz przykład takiej twojej metody? StreamingListener to jest klasa po twojej stronie?

0
GotowanaKukurydza napisał(a):

ale zaraz, XTB wystawia web api to jak tam jest klasa, która wystawia void wypisująca na konsole? a potem podajesz przykład takiej twojej metody? StreamingListener to jest klasa po twojej stronie?

StreamingListener to jest klasa z XTB, natomiast to co co jet w ciele metody receiveTickRecord(STickRecord tickRecord) to moja implementacja. Chciałem ćwiczebnie zobaczyć jak wygląda wyciąganie danych z api.
Jeśli chodzi o dane pojedyncze czy historyczne to NIE MA problemu, wszystkie metody zwracają odpowiednie typy. Problem mam właśnie z danymi w czasie rzeczywistym.

ps.Dzięki wszystkim za odzew.

1

Pobieranie danych w czasie rzeczywistym różni się od pobierania danych historycznych dlatego bo to nie Ty decydujesz kiedy pobierasz dane tylko 'czas rzeczywisty'. StreamingListener to prawdopodobnie interfejs dostępny w API. Powinieneś taki interfejs zaimplementować, a następnie zarejestrować instancję tej klasy gdzieś w API. W momencie gdy nastąpi wydarzenie na które się zarejestrowałeś, XTB wywoła Twoją metodę receiveTickRecord() i wtedy wykona się przetwarzanie, które zaimplementowałeś.

0

Ok, teraz rozumiem. To tak jak napisał @JacekPs , standardowe ( a przynajmniej te które dotąd widziałem ) rozwiązania z danymi finansowymi w czasie rzeczywistym to wrzucenie do kolekcji a potem odczyt z tej kolekcji co jakiś czas z innego wątku.

1

@i1010011010: A jaki cel chcesz osiągnąć? Operowanie na wartości nie jest celem samym w sobie. Zgaduję, że dopier się uczysz i wzorzec Listenera jest dla Ciebie mało intuicyjny.
W metodzie którą podałeś masz już wartość i możesz na niej operować - możesz dodać ją do jakiejś kolekcji, wyświetlić użytkownikowi czy zrobić cokolwiek innego. Na pewno nie idź w uchwyt do System.out :D

0
VeloxDigitis napisał(a):

@i1010011010: A jaki cel chcesz osiągnąć? Operowanie na wartości nie jest celem samym w sobie. Zgaduję, że dopier się uczysz i wzorzec Listenera jest dla Ciebie mało intuicyjny.
W metodzie którą podałeś masz już wartość i możesz na niej operować - możesz dodać ją do jakiejś kolekcji, wyświetlić użytkownikowi czy zrobić cokolwiek innego. Na pewno nie idź w uchwyt do System.out :D

Generalnie chce operować na wartościach (np tworzyć strategie z kilku instrumentów). jak zauważyłeś w samej metodzie mogę operować na wartości. Argument tickrecord w metodzie pozwala na na pobranie tez innych informacji w czasie rzeczywistym(AskPrice, BidPrice,Time,Volume etc.), ale musi być jako argument tej metody, inaczej wywala komunikat "listener method not implemented for receiveTickRecord(tickRecord)".
Celem głównym oprócz operowania wartościami jest wyświetlanie je na wykresie. Projekt tworzę w springu i jeśli chodzi o dane historyczne i dane nie w czasie rzeczywistym to udało się je wyswietlić. Jednak do controllera jako model attribute muszę dać oject a nie void. Chyba że jest jakiś na to sposób :)

0
i1010011010 napisał(a):

Celem głównym oprócz operowania wartościami jest wyświetlanie je na wykresie. Projekt tworzę w springu i jeśli chodzi o dane historyczne i dane nie w czasie rzeczywistym to udało się je wyswietlić. Jednak do controllera jako model attribute muszę dać oject a nie void. Chyba że jest jakiś na to sposób :)

Przekaż do tego Listenera jakiś obiekt (np. serwis odpowiedzialny za agregację danych) przez konstruktor i w metodzie receiveTickRecord wywołaj odpowiednią metodę ze swojego serwisu np. dataService.registerPrice(tickRecord.getAsk()) (na pewno wymyślisz lepsze nazwy, ja nie znam domeny). Potem wyświetlając wykres będziesz mógł pobrać sobie wszystkie zapisane ceny z tego serwisu

0

@Charles_Ray: Chodzi mi o klasy w pakiecie streamlistenerimpl. Nie wiem nic o server-side ani o websockecie.
Jeśli chodzi pisanie kodu to chętnie usłyszę jak go lepiej napisać, zdaje sobie sprawę, mówiąc dyplomatycznie :) - że można go dużo lepiej napisać.
Każda wskazówka ww tematach jest dla mnie na wagę złota.
kod - https://github.com/i1010011010/fxBoot
dokumentacja XTB - http://developers.xstore.pro/api

0

Zamiast bawić się w kolejki i Future możesz spróbować się pobawić Guavowym EventBusem i wyniki operacji z listenera przesyłać na szynę zdarzeń jako event.

0

moe szukasz SynchronousQueue ? Jak chcesz pozniej rysowac te wykresy? Za pomoca jakiej technologii? Czy to ma byc na potrzeby tylko rysowania wykresu czy moze chcesz w przyszlosci tym grac, czyli odpowiadac dla xtb?

0
i1010011010 napisał(a):

Chcę w testować strategie a potem zrobić automatyczny trading. Apka jest w springu, na razie udaje mi się narysować wykres z danymi historycznymi(szablon z canvasjs jako strona jsp)

Wedlug mnie tam nie potrzebnie implementujesz na kazda ceche ten ich interface. Czemu by nie zbudowac swojego domenowego Tick calego z ask,bid,timestamp i volume na podstawie STickRecord?

Jesli chcesz przetestowac strategie to musisz poszukac innego providera niz xtb:

PERIOD_M1 --- <0-1) month, i.e. one month time
PERIOD_M30 --- <1-7) month, six months time
PERIOD_H4 --- <7-13) month, six months time
PERIOD_D1 --- 13 month, and earlier on

To zdecydowanie za waski zakres zeby cokolwiek powiedziec o skutecznosci strategii.

Co do samego design'u to masz conajmniej dwie mozliwosci - mozesz przekazac jakis callback do tej klasy ktora odbiera Tick'a i wtedy moze byc void bo funkcja nic nie musi zwracac ale to slaby design poniewaz powiazesz ze soba logike, odbieranie tickow i jeszcze wysylanie odpowiedz - bedziesz mial takiego betonowego klocka z ktorym nic sie nie da zrobic po tygodniu zycia projektu. Drugie podejscie, nieco trudniejsze ale pozwoli Ci to nie narobic balaganu i roszerzac ta aplikacje w przyszlosci to jakis sensowny podzial. Widze tutaj interface'y Strategy, Provider i Player.

Strategy - to Twoja logika gry, jakas funkcja Tick -> Decision (musisz pamietac ze bedziesz operowal na tick'ach)
Provider - dostawca ticków, w zaleznosci od sytuacji podrzcuasz innego. Do testowania strategii bedzie to jakies zrodlo z internetu a podczas realnej gry bedzie to XtbProvider, to bylaby funkcja Ticker -> Stream<Tick>

Player - cos co na podstawie decyzji Strategy cos robi, np dla symulacji, moglby zliczac wszystkie statystyki ktorych potrzebujesz zeby ocenic skutecznosc strategii, z kolei na real-game bylby po prostu klientem protokolu web-socket

Dzieki takiemu podzialowi, zauwaz ze po przetestowaniu strategii, wystarczy przepiąc Provider'a i program jest gotowy do gry na real. Generalnie mozesz wtedy wszystko ze soba mieszac - tzn grac na roznych instrumentach, u roznych brokerow roznymi strategiami tylko podmieniajac implementacje. A co wazniejsze, pozwoli Ci to odowiedno przydzielic zasoby (wątki) do poszczegolnych czesci programu

Co do rysowania wykresu, to przy wiekszej ilosci danych ten REST kleknie wiec albo wystawiasz sobie sam serwer websocket i piszesz klienta i tworzysz sobie GUI webowe (tak jak teraz zreszta) albo od razu mozesz pisac to np w Cythonie jesli bedzie problem z wydajnoscia.

Do ogarniecia jeszcze masz wielowatkowosc i cache dla tickow historycznych ale to juz temat na doktorat : P

1

Nie potrzebujesz AtomicReference, uruchom na jednym wątku mapowanie na Tick + wrzucanie na kolejke a w watku z psvm odbieraj z tej kolejki

0

jak odpaliłem to mi wychodzi takie coś(diagnostycznie dodałem sysouty) instrument DAX

StreamingListenerService.receiveTickRecord() -> STickRecord [ask=null, bid=null, askVolume=null, bidVolume=null, high=null, low=null, spreadRaw=0.0, spreadTable=0.0, symbol=null, quoteId=0, level=0, timestamp=0]
====================================================================================
StreamingListenerService.getTickRecord() -> STickRecord [ask=null, bid=null, askVolume=null, bidVolume=null, high=null, low=null, spreadRaw=0.0, spreadTable=0.0, symbol=null, quoteId=0, level=0, timestamp=0]
StreamingListenerService.getTickRecord() -> STickRecord [ask=null, bid=null, askVolume=null, bidVolume=null, high=null, low=null, spreadRaw=0.0, spreadTable=0.0, symbol=null, quoteId=0, level=0, timestamp=0]
StreamingListenerService.receiveTickRecord() -> STickRecord [ask=null, bid=null, askVolume=null, bidVolume=null, high=null, low=null, spreadRaw=0.0, spreadTable=0.0, symbol=null, quoteId=0, level=0, timestamp=0]
====================================================================================
StreamingListenerService.getTickRecord() -> STickRecord [ask=null, bid=null, askVolume=null, bidVolume=null, high=null, low=null, spreadRaw=0.0, spreadTable=0.0, symbol=null, quoteId=0, level=0, timestamp=0]
StreamingListenerService.getTickRecord() -> STickRecord [ask=null, bid=null, askVolume=null, bidVolume=null, high=null, low=null, spreadRaw=0.0, spreadTable=0.0, symbol=null, quoteId=0, level=0, timestamp=0]
StreamingListenerService.getTickRecord() -> STickRecord [ask=null, bid=null, askVolume=null, bidVolume=null, high=null, low=null, spreadRaw=0.0, spreadTable=0.0, symbol=null, quoteId=0, level=0, timestamp=0]
MAIN -> STickRecord [ask=null, bid=null, askVolume=null, bidVolume=null, high=null, low=null, spreadRaw=0.0, spreadTable=0.0, symbol=null, quoteId=0, level=0, timestamp=0]
StreamingListenerService.receiveTickRecord() -> STickRecord [ask=10307.29, bid=10238.7, askVolume=1, bidVolume=2, high=10284.47, low=9843.96, spreadRaw=68.59, spreadTable=6859.0, symbol=BITCOIN, quoteId=5, level=0, timestamp=1595847660710]
====================================================================================
StreamingListenerService.getTickRecord() -> STickRecord [ask=10307.29, bid=10238.7, askVolume=1, bidVolume=2, high=10284.47, low=9843.96, spreadRaw=68.59, spreadTable=6859.0, symbol=BITCOIN, quoteId=5, level=0, timestamp=1595847660710]
StreamingListenerService.receiveTickRecord() -> STickRecord [ask=10308.85, bid=10240.15, askVolume=1, bidVolume=2, high=10284.47, low=9843.96, spreadRaw=68.7, spreadTable=6870.0, symbol=BITCOIN, quoteId=5, level=0, timestamp=1595847661234]
====================================================================================
StreamingListenerService.getTickRecord() -> STickRecord [ask=10308.85, bid=10240.15, askVolume=1, bidVolume=2, high=10284.47, low=9843.96, spreadRaw=68.7, spreadTable=6870.0, symbol=BITCOIN, quoteId=5, level=0, timestamp=1595847661234]
StreamingListenerService.receiveTickRecord() -> STickRecord [ask=10309.37, bid=10240.39, askVolume=1, bidVolume=2, high=10284.47, low=9843.96, spreadRaw=68.98, spreadTable=6898.0, symbol=BITCOIN, quoteId=5, level=0, timestamp=1595847661738]
====================================================================================
StreamingListenerService.getTickRecord() -> STickRecord [ask=10309.37, bid=10240.39, askVolume=1, bidVolume=2, high=10284.47, low=9843.96, spreadRaw=68.98, spreadTable=6898.0, symbol=BITCOIN, quoteId=5, level=0, timestamp=1595847661738]
StreamingListenerService.receiveTickRecord() -> STickRecord [ask=10311.38, bid=10242.15, askVolume=1, bidVolume=2, high=10284.47, low=9843.96, spreadRaw=69.23, spreadTable=6923.0, symbol=BITCOIN, quoteId=5, level=0, timestamp=1595847662253]
====================================================================================
StreamingListenerService.getTickRecord() -> STickRecord [ask=10311.38, bid=10242.15, askVolume=1, bidVolume=2, high=10284.47, low=9843.96, spreadRaw=69.23, spreadTable=6923.0, symbol=BITCOIN, quoteId=5, level=0, timestamp=1595847662253]
StreamingListenerService.receiveTickRecord() -> STickRecord [ask=10311.41, bid=10242.33, askVolume=1, bidVolume=2, high=10284.47, low=9843.96, spreadRaw=69.08, spreadTable=6908.0, symbol=BITCOIN, quoteId=5, level=0, timestamp=1595847662771]
====================================================================================
StreamingListenerService.getTickRecord() -> STickRecord [ask=10311.41, bid=10242.33, askVolume=1, bidVolume=2, high=10284.47, low=9843.96, spreadRaw=69.08, spreadTable=6908.0, symbol=BITCOIN, quoteId=5, level=0, timestamp=1595847662771]
StreamingListenerService.receiveTickRecord() -> STickRecord [ask=10311.23, bid=10242.79, askVolume=1, bidVolume=2, high=10284.47, low=9843.96, spreadRaw=68.44, spreadTable=6844.0, symbol=BITCOIN, quoteId=5, level=0, timestamp=1595847663278]
====================================================================================

main pojawia się tylko raz i w dodatku z nullami
Oczywiście apka się nie zamyka i nadaj ciągnie dane bez nulli z serwera, ale sysout z main się nie pojawia.

1
i1010011010 napisał(a):

ustawiam kolejkę jako zmienna klasową, potem w metodzie receiveTickRecord kłatę cały tickRecord do kolejki. Potem w tej samej metodzie ustawiam getTickRecord() W metodzie getTickRecord wyciągam z kolejki metodą take i przypisuje to do Wartości StickRecords record = recordQueue.take. i zwracam record. jak robię debuggerem to w pierwszym przebiegu wyświetla mi w pvsm record(jednak wszystko jako nulle) w następnych przebiegach wyświetla mi już zawartość STickRecord jednak krąży w obrębie pierwszych dwóch metod(są tam diagnostyczne sysouty)

Chyba cos tam namieszales, ciezko bez kodu cos powiedziec ale cos takiego powinienes miec:

psvm {
   Queue queue  ///
   XtbTickStreamListener receiver = new XtbStreamListener(queue);
    TickConsumer tickConsumer = new TickConsumer(queue)
    receiver.start();
    tickConsumer.start();
}

Czyli tworzysz kolejke ktora beda wspoldzielily dwie klasy, te dwie klasy reprezentuja watki, TickConsumer w petli odbiera z kolejki. To pseudocode wersji pogladowej ofc

0

Na pewno coś namieszałem inaczej by mi wyszło:)
to jest klasa która "łapie dane"

public class StreamingListenerService extends StreamingListener {
    private BlockingQueue<STickRecord> recordQueue;
    
    public StreamingListenerService() {
        recordQueue = new LinkedBlockingDeque<>();
    }

    @Override
    public void receiveTickRecord(STickRecord tickRecord) {
        if (tickRecord.getLevel() == 0) {
            try {
                recordQueue.put(tickRecord);
                System.out.println("StreamingListenerService.receiveTickRecord() -> " + recordQueue);
                System.out.println("TickRecords.size -> '"+ recordQueue.size());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            getFromQueue(tickRecord);
        }
    }

    public STickRecord getFromQueue(STickRecord record) {
        try {
            record = recordQueue.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("StreamingListenerService.getTickRecord() -> " + record);
        System.out.println("======================================StreamingListenerService==============================================");
        return record;
    }
}

To jest mój serwis do któego chce przekazać te dane

public class StreamDataServiceImpl {
    private STickRecord record;
    private StreamingListenerService service;



    public StreamDataServiceImpl() {
        service = new StreamingListenerService();
        ResponseService.getStreamResponse("DE30",service);
        record = new STickRecord();

    }
    public STickRecord getStreamTickRecord() throws InterruptedException {
        ResponseService.getStreamResponse("BITCOIN", new StreamingListenerService()); //Statyczna metoda, która służy do logowania do servera xtb oraz subskrybowania instrumentu
        service.getFromQueue(new STickRecord());
        System.out.println("StreamDataServiceImpl.getStreamTickRecord() -> "+record);
        return record;
    }
}

A to klasa uruchomieniowa
ponieważ wybrałem kompleksową informację ze STickRecord tu chcę wyświetlić tylko cenę ask( lub potem procesować np. bid\ask etc.). Ponieważ chcę generanie żeby wyłuskać typowana wartość z metody public void receiveTickRecord(STickRecord tickRecord)

public class TestStreamMethods {

    public static void main(String[] args) throws InterruptedException {

        StreamDataServiceImpl service = new StreamDataServiceImpl();
        System.out.println("TestStreamMethods.main() -> "+service.getStreamTickRecord().getAsk());
    }
}
1
    public STickRecord getFromQueue(STickRecord record) {
        try {
            record = recordQueue.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("StreamingListenerService.getTickRecord() -> " + record);
        System.out.println("======================================StreamingListenerService==============================================");
        return record;
    }

W Javie zawsze przez wartosc sa przekazywane argumenty do metod, to nie zadziała. Nie wspoldzielisz kolejki tak jak poakzywalem w przykladzie, tylko inicjalizujesz w jednym serwisie a drugi nie ma do niej dostepu.
Generalnie samo podejscie jest słabe jesli chciałbyś na żądanie pobrać tick'a. A one z natury przychodzą asynchronicznie (tzn nie wiesz kiedy pojawi sie Tick), wiec teraz jak to sobie wyobrazasz? Jak wywołasz raz getter a beda dwa ticki na kolejce? Albo jeszcze zadnego nie bedzie? A pozniej co ile czasu i jakim mechanizmem bedziesz odpytywal ta kolejke?

Wersja budzetowa i latwa w implementacji to obczaj sobie reactor.io biblioteke i chyba nawet w Springu jest integracja z protokolem websocket tej biblioteki. Wtedy bedziesz mogl mniej wiecej osiagnac to co, co z tego co widze zamierzasz, bedziesz mial takiego Flux<Tick>, cos jak Stream<Tick> dostepne.

Generalnie jeszcze kiedys daloby to sie osiagnac tylko callback'ami, jeszcze w Java 7 to robilo sie taki callback hell, ze przesuwales 3 szerokosci podkladki myszka suwak zeby sie przebic przez callback'i do logiki. W Java 8 dodali kolejna implemetnacje interface'u Future, mianowicie CompletableFuture ktory pozwolil ten callback hell ukryc, za pomoca operatorow thenApply, thenAccept (cos jak map, andThen) podczepiasz callback'i. Z CF'em nadal jest od groma problemów, dlatego ludzie poszli dalej i zrobili wlasnie biblioteke reactor.io ktora przykrywa bolączki standardu Javy i w zamian mamy bolączki reactor'a ale z tym da sie juz nieco wiecej zrobic. Duzo nauki przed Toba jesli chcesz to dobrze zaprogramowac

1 użytkowników online, w tym zalogowanych: 0, gości: 1