@@ -7,14 +7,15 @@ var JDate = java.util.Date;
77var BigDecimal = java . math . BigDecimal ;
88
99var fetcher ;
10- var s ;
10+ var isin ;
11+ var exchangeCodeMap = { }
1112
1213function getAPIVersion ( ) {
1314 return "1" ;
1415} ;
1516
1617function getVersion ( ) {
17- return "2025-01-31 " ;
18+ return "2026-03-21 " ;
1819} ;
1920
2021function getName ( ) {
@@ -28,90 +29,151 @@ function getURL() {
2829function prepare ( fetch , search , startyear , startmon , startday , stopyear , stopmon , stopday ) {
2930 Logger . info ( "Configuring..." ) ;
3031 fetcher = fetch ;
31- s = search ;
32+ isin = search ;
3233
33- var cfgliste = new ArrayList ( ) ;
34+ const webClient = fetcher . getWebClient ( false ) ;
35+ webClient . getOptions ( ) . setThrowExceptionOnFailingStatusCode ( false ) ;
36+
37+ try {
38+ Logger . debug ( "Requesting time ranges" ) ;
39+ //const pageTimeRanges = webClient.getPage("https://component-api.wertpapiere.ing.de/api/v1/components-ng/chart?isins=" + isin);
40+ const pageTimeRanges = webClient . getPage ( "https://component-api.wertpapiere.ing.de/api/v1/components/charttool/" + isin ) ;
41+ const responseTimeRanges = JSON . parse ( pageTimeRanges . getWebResponse ( ) . getContentAsString ( ) ) ;
42+
43+ Logger . debug ( "Requesting exchanges and currency" ) ;
44+ const pageExchanges = webClient . getPage ( "https://component-api.wertpapiere.ing.de/api/v1/instrument-header?isinOrSearchTerm=" + isin + "&isKnownIsin=true&includeAvailableExchanges=true" ) ;
45+ const responseExchanges = JSON . parse ( pageExchanges . getWebResponse ( ) . getContentAsString ( ) ) ;
46+ }
47+ catch ( error ) {
48+ Logger . error ( "ISIN " + isin + " nicht gefunden bei " + getName ( ) ) ;
49+ }
3450
3551 // Zeitraum
36- var timeRange = new Packages . jsq . config . Config ( "Zeitraum" ) ;
37- timeRange . addAuswahl ( "Eine Woche" , new String ( "OneWeek" ) ) ;
38- timeRange . addAuswahl ( "Ein Monat" , new String ( "OneMonth" ) ) ;
39- timeRange . addAuswahl ( "Ein Jahr" , new String ( "OneYear" ) ) ;
40- timeRange . addAuswahl ( "Fuenf Jahre" , new String ( "FiveYears" ) ) ;
41- timeRange . addAuswahl ( "Maximum" , new String ( "Maximum" ) ) ;
52+ var historyConfig = new Packages . jsq . config . Config ( "Historie" ) ;
53+ const periods = responseTimeRanges [ "chartPeriodTranslations" ] ;
54+ periods . forEach ( period => {
55+ if ( period [ "chartPeriod" ] != "Intraday" ) {
56+ historyConfig . addAuswahl ( period [ "translation" ] , period [ "chartPeriod" ] ) ;
57+ }
58+ } ) ;
59+
60+ // Kursdetails: Hoch,Tief, Eröffnung
61+ var ohlcConfig = new Packages . jsq . config . Config ( "Kursdetails" ) ;
62+ ohlcConfig . addAuswahl ( "Keine" , new Boolean ( false ) ) ;
63+ ohlcConfig . addAuswahl ( "Hoch-/Tief-/Eröffnungskurse" , new Boolean ( true ) ) ;
4264
4365 // Handelsplatz
44- var exchange = new Packages . jsq . config . Config ( "Handelsplatz" ) ;
45- exchange . addAuswahl ( "Direkthandel" , new String ( "2779" ) ) ;
46- exchange . addAuswahl ( "Xetra" , new String ( "44" ) ) ;
47- exchange . addAuswahl ( "Frankfurt" , new String ( "13" ) ) ;
48- exchange . addAuswahl ( "Duesseldorf" , new String ( "14" ) ) ;
49- exchange . addAuswahl ( "Muenchen" , new String ( "15" ) ) ;
50- exchange . addAuswahl ( "Stuttgart" , new String ( "16" ) ) ;
51- exchange . addAuswahl ( "Hamburt" , new String ( "17" ) ) ;
52- exchange . addAuswahl ( "Berlin" , new String ( "18" ) ) ;
53- exchange . addAuswahl ( "Hannover" , new String ( "19" ) ) ;
54- exchange . addAuswahl ( "USA (Nasdaq)" , new String ( "537" ) ) ;
55-
56- cfgliste . add ( timeRange ) ;
57- cfgliste . add ( exchange ) ;
66+ const exchanges = responseExchanges [ "exchanges" ] ;
67+ Logger . debug ( "Found " + exchanges . length + " exchanges" ) ;
68+
69+ var exchangeConfig = new Packages . jsq . config . Config ( "Handelsplatz" ) ;
70+ exchanges . forEach ( exchange => {
71+ exchangeConfig . addAuswahl ( exchange [ "exchangeName" ] , exchange [ "exchangeCode" ] ) ;
72+ Logger . debug ( "currency " + exchange [ "currencySymbol" ] + " found at " + exchange [ "exchangeCode" ] + " (" + exchange [ "exchangeName" ] + ")" ) ;
73+ // remember infos of exchange for process()
74+ exchangeCodeMap [ exchange [ "exchangeCode" ] ] = exchange ;
75+ } ) ;
76+
77+ var cfgliste = new ArrayList ( ) ;
78+ cfgliste . add ( historyConfig ) ;
79+ cfgliste . add ( ohlcConfig ) ;
80+ cfgliste . add ( exchangeConfig ) ;
5881
5982 return cfgliste ;
6083}
6184
6285function process ( config ) {
63- var timeRange = "OneYear" ;
64- var exchange = "2779" // Direkthandel
86+ // default config
87+ var history = "OneMonth" ;
88+ var ohlc = "" ;
89+ var exchange = {
90+ "exchangeCode" : "TGT" ,
91+ "exchangeName" : "Direkthandel" ,
92+ "exchangeId" : 2779 ,
93+ "currencySymbol" : "EUR" ,
94+ "currencyId" : 814
95+ } ;
96+ // read from saved config
6597 for ( i = 0 ; i < config . size ( ) ; i ++ ) {
6698 var cfg = config . get ( i ) ;
6799 Logger . info ( cfg . toString ( ) ) ;
68100 for ( j = 0 ; j < cfg . getSelected ( ) . size ( ) ; j ++ ) {
69101 var o = cfg . getSelected ( ) . get ( j ) ;
70- if ( cfg . getBeschreibung ( ) . equals ( "Zeitraum " ) ) {
71- timeRange = o . getObj ( ) ;
102+ if ( cfg . getBeschreibung ( ) . equals ( "Historie " ) ) {
103+ history = o . getObj ( ) ;
72104 } else if ( cfg . getBeschreibung ( ) . equals ( "Handelsplatz" ) ) {
73- exchange = o . getObj ( ) ;
105+ const exchangeCode = o . getObj ( ) ;
106+ if ( exchangeCode in exchangeCodeMap ) {
107+ exchange = exchangeCodeMap [ exchangeCode ] ;
108+ Logger . debug ( "currency at exchange " + exchangeCode + " is " + exchange [ "currencySymbol" ] ) ;
109+ }
110+ } else if ( cfg . getBeschreibung ( ) . equals ( "Kursdetails" ) ) {
111+ ohlc = o . getObj ( ) . valueOf ( ) ? "&ohlc=true" : "" ;
74112 }
75113 }
76114 }
77115
78- var webClient = fetcher . getWebClient ( false ) ;
79-
80- Logger . info ( "Fetching metadata for " + s ) ;
81-
82- // Fetch ID
83- var page = webClient . getPage ( "https://component-api.wertpapiere.ing.de/api/v1/components/instrumentheader/" + s ) ;
84- var metadata = JSON . parse ( page . getWebResponse ( ) . getContentAsString ( ) ) ;
85- var id = metadata [ "id" ] ;
86-
87- Logger . info ( "Stock " + s + " has ID " + id + ", fetching prices..." ) ;
88116
89117 // Fetch data
90- page = webClient . getPage ( "https://component-api.wertpapiere.ing.de/api/v1/charts/shm/" + id + "?timeRange=" + timeRange + "&exchangeId=" + exchange + "¤cyId=814" ) ;
91- var response = JSON . parse ( page . getWebResponse ( ) . getContentAsString ( ) ) ;
92- var data = response [ "instruments" ] [ 0 ] [ "data" ] ;
118+ var res = new ArrayList ( ) ;
93119
94- Logger . info ( "Fetched " + data . length + " results." ) ;
120+ Logger . info ( "Fetching history " + history + " of " + isin + " at " + exchange [ "exchangeCode" ] ) ;
121+ const webClient = fetcher . getWebClient ( false ) ;
122+ //const url = "https://component-api.wertpapiere.ing.de/api/v1/charts/shm/" + isin + "?timeRange="+ history + "&exchangeId=" + exchangeId + "¤cyId=" + currencyId;
123+ const url = "https://component-api.wertpapiere.ing.de/api/v1/charts/charttooldata/" + isin + "?timeRange=" + history + "&exchangeId=" + exchange [ "exchangeId" ] + "&exchangeCode=" + exchange [ "exchangeCode" ] + "¤cyId=" + exchange [ "currencyId" ] + ohlc ;
124+ Logger . debug ( "request " + url )
125+ const page = webClient . getPage ( url ) ;
126+ const response = JSON . parse ( page . getWebResponse ( ) . getContentAsString ( ) ) ;
127+ const data = response [ "instruments" ] [ 0 ] [ "data" ] ;
128+ const keys = response [ "instruments" ] [ 0 ] [ "keys" ] ; // meaning of elements in data ordered in the same way
129+ const keyMapping = { "x" : "date" ,
130+ "y" : "last" ,
131+ "open" : "first" ,
132+ "high" : "high" ,
133+ "low" : "low" ,
134+ "close" : "last"
135+ } ; // translate keys to internal id in Datacontainer
136+ const noMapping = "nomapping"
95137
96- if ( data . length === 0 ) {
97- fetcher . setHistQuotes ( new ArrayList ( ) ) ;
98- return ;
99- }
138+ Logger . info ( "Fetched " + data . length + " results." ) ;
100139
101- var res = new ArrayList ( ) ;
102- for ( var i = 1 ; i < data . length ; i ++ ) {
103- var dataPoint = data [ i - 1 ] ;
104- var date = new JDate ( dataPoint [ 0 ] ) ;
105- var price = new BigDecimal ( dataPoint [ 1 ] ) ;
106- var newDate = new JDate ( data [ i ] [ 0 ] ) ;
107- // Ensure there's only one result per day
108- if ( date . getDay ( ) != newDate . getDay ( ) ) {
140+ Logger . debug ( "keys=" + keys ) ;
141+ let oldDate = new JDate ( ) ;
142+ data . forEach ( row => {
143+ // transform each row to a dict to match values with keys
144+ item = Object . fromEntries (
145+ keys . map ( ( key , i ) => [ ( key in keyMapping ? keyMapping [ key ] : noMapping ) , row [ i ] ] )
146+ ) ;
147+ Logger . trace ( "item=" + JSON . stringify ( item ) ) ;
148+ // no need to consider response["instruments"][0]["currentTimezoneOffset"], since quotes are given in UTC
149+ const date = new JDate ( item [ "date" ] ) ;
150+
151+ // Ensure there's only one result per day; assume historyItems are sorted by date
152+ if ( res . isEmpty ( ) || ( date . getDate ( ) != oldDate . getDate ( ) ) ) {
109153 var dc = new Packages . jsq . datastructes . Datacontainer ( ) ;
110- dc . put ( "currency" , "EUR" ) ;
111- dc . put ( "date" , date ) ;
112- dc . put ( "last" , price ) ;
113- res . add ( dc ) ;
154+ Object . entries ( item ) . forEach ( ( [ mappedKey , value ] ) => {
155+ if ( ! [ "date" , noMapping ] . includes ( mappedKey ) ) {
156+ // add only values, where key could be mapped and is not date
157+ dc . put ( mappedKey , new BigDecimal ( value ) ) ;
158+ }
159+ } ) ;
160+
161+ // if we found any value for mapable keys, we add the dc
162+ if ( ! dc . getMap ( ) . isEmpty ( ) ) {
163+ dc . put ( "currency" , exchange [ "currencySymbol" ] ) ;
164+ dc . put ( "date" , date ) ;
165+ res . add ( dc ) ;
166+ }
114167 }
168+ oldDate = date ;
169+ } ) ;
170+
171+ if ( res . length > 0 ) {
172+ Logger . info ( "Received " + res . length + " historic quotes." ) ;
115173 }
174+ else {
175+ Logger . error ( "No historic quotes found. " + ( data . length > 0 ? "Maybe key mapping has changed." : "" ) ) ;
176+ }
177+
116178 fetcher . setHistQuotes ( res ) ;
117179}
0 commit comments