@@ -19,7 +19,10 @@ public class HistoricalMarketService implements AutoCloseable {
1919 private final Map <String , HistoricalData > historicalCache ;
2020 @ Getter
2121 private final MarketDataProvider provider ;
22+
2223 private final ExecutorService requestExecutor ;
24+ private final ExecutorService fetchExecutor ;
25+
2326 private final int maxRetries ;
2427 private volatile boolean isInitialized = false ;
2528 private final Object initLock = new Object ();
@@ -30,6 +33,7 @@ public HistoricalMarketService(MarketDataProvider provider, int maxRetries, Path
3033 this .provider = provider ;
3134 this .historicalCache = new ConcurrentHashMap <>();
3235 this .requestExecutor = Executors .newSingleThreadExecutor ();
36+ this .fetchExecutor = Executors .newFixedThreadPool (Runtime .getRuntime ().availableProcessors ());
3337 this .maxRetries = maxRetries ;
3438 this .cacheDirectory = Optional .ofNullable (cacheDirectory );
3539
@@ -166,28 +170,39 @@ public CompletableFuture<Map<String, List<MarketDataPoint>>> fetchHistoricalData
166170 LocalDateTime start ,
167171 LocalDateTime end ) {
168172
169- return CompletableFuture .supplyAsync (() -> {
170- if (!isInitialized ) {
171- throw new IllegalStateException ("HistoricalMarketService not initialized" );
172- }
173-
174- Map <String , List <MarketDataPoint >> result = new ConcurrentHashMap <>();
173+ if (!isInitialized ) {
174+ throw new IllegalStateException ("HistoricalMarketService not initialized" );
175+ }
175176
176- for (String ticker : tickers ) {
177- HistoricalData cachedData = historicalCache .get (ticker );
178- if (cachedData == null && cacheDirectory .isPresent ()) {
179- // Try loading from file cache as fallback if caching is enabled
180- cachedData = loadFromCache (ticker , start , end );
181- if (cachedData == null ) {
182- throw new IllegalStateException ("No cached data for ticker: " + ticker );
177+ Map <String , List <MarketDataPoint >> result = new ConcurrentHashMap <>();
178+ List <CompletableFuture <Void >> futures = new ArrayList <>();
179+
180+ for (String ticker : tickers ) {
181+ CompletableFuture <Void > future = CompletableFuture .runAsync (() -> {
182+ try {
183+ HistoricalData cachedData = historicalCache .get (ticker );
184+ if (cachedData == null && cacheDirectory .isPresent ()) {
185+ cachedData = loadFromCache (ticker , start , end );
186+ if (cachedData == null ) {
187+ throw new IllegalStateException ("No cached data for ticker: " + ticker );
188+ }
189+ historicalCache .put (ticker , cachedData );
183190 }
184- historicalCache .put (ticker , cachedData );
191+ if (cachedData != null ) {
192+ result .put (ticker , cachedData .getDataPoints (start , end ));
193+ } else {
194+ throw new IllegalStateException ("No data available for ticker: " + ticker );
195+ }
196+ } catch (Exception e ) {
197+ throw new CompletionException (e );
185198 }
186- result .put (ticker , cachedData .getDataPoints (start , end ));
187- }
199+ }, fetchExecutor );
188200
189- return result ;
190- });
201+ futures .add (future );
202+ }
203+
204+ return CompletableFuture .allOf (futures .toArray (new CompletableFuture [0 ]))
205+ .thenApply (v -> result );
191206 }
192207
193208 public void clearCache () {
@@ -211,12 +226,17 @@ public void clearCache() {
211226 @ Override
212227 public void close () {
213228 requestExecutor .shutdown ();
229+ fetchExecutor .shutdown ();
214230 try {
215231 if (!requestExecutor .awaitTermination (30 , TimeUnit .SECONDS )) {
216232 requestExecutor .shutdownNow ();
217233 }
234+ if (!fetchExecutor .awaitTermination (30 , TimeUnit .SECONDS )) {
235+ fetchExecutor .shutdownNow ();
236+ }
218237 } catch (InterruptedException e ) {
219238 requestExecutor .shutdownNow ();
239+ fetchExecutor .shutdownNow ();
220240 Thread .currentThread ().interrupt ();
221241 }
222242 }
0 commit comments