Documentation, examples and further information of the ta4j project
This project is maintained by ta4j Organization
Refactor BarSeriesDataSource implementations to separate data retrieval (I/O) from data parsing/transformation. This will improve maintainability, testability, and allow for better composition and reuse of components.
Status: design remains proposed; current ta4j-examples architecture is still BarSeriesDataSource + concrete HTTP/file datasource hierarchies.
Current baseline:
ta4jexamples.datasources.file.AbstractFileBarSeriesDataSource.ta4jexamples.datasources.http.AbstractHttpBarSeriesDataSource.CoinbaseHttpBarSeriesDataSource, YahooFinanceHttpBarSeriesDataSource, CsvFileBarSeriesDataSource, and JsonFileBarSeriesDataSource.The proposed DataRetrievalClient and DataFormatMapper interfaces are not yet present in ta4j-examples.
DECISION: File search and caching logic should live in DataRetrievalClient (Option A)
Rationale:
Location: ta4j-examples/src/main/java/ta4jexamples/datasources/client/DataRetrievalClient.java
package ta4jexamples.datasources.client;
import java.io.InputStream;
/**
* Handles WHERE data comes from (I/O layer).
* Responsible for retrieving raw data from various sources (files, HTTP, databases, etc.).
*
* Implementations handle:
* - File system I/O
* - HTTP requests
* - Database queries
* - Caching (via CacheableDataRetrievalClient)
* - File search patterns (for file-based clients)
*/
public interface DataRetrievalClient {
/**
* Retrieves raw data as a string from the specified source.
*
* @param sourceIdentifier - could be filename, URL, query params, resource path, etc.
* @return raw data string, or null if not found
* @throws DataRetrievalException if retrieval fails
*/
String retrieveData(String sourceIdentifier) throws DataRetrievalException;
/**
* Retrieves raw data as an InputStream.
* Caller is responsible for closing the stream.
*
* @param sourceIdentifier - source to retrieve from
* @return InputStream containing raw data, or null if not found
* @throws DataRetrievalException if retrieval fails
*/
InputStream retrieveDataStream(String sourceIdentifier) throws DataRetrievalException;
/**
* Checks if the client can handle the given source identifier.
*
* @param sourceIdentifier - source to check
* @return true if this client can handle the source
*/
boolean canHandle(String sourceIdentifier);
}
Location: ta4j-examples/src/main/java/ta4jexamples/datasources/mapper/DataFormatMapper.java
package ta4jexamples.datasources.mapper;
import org.ta4j.core.BarSeries;
import java.io.InputStream;
import java.time.Duration;
/**
* Handles HOW to parse/transform data (parsing layer).
* Responsible for converting raw data strings into BarSeries objects.
*
* Implementations handle:
* - JSON parsing (Yahoo Finance, Binance, Coinbase formats)
* - CSV parsing (various CSV formats)
* - Format-specific transformations
* - Error handling during parsing
*/
public interface DataFormatMapper {
/**
* Maps raw data string to a BarSeries.
*
* @param rawData - the raw data string (JSON, CSV, etc.)
* @param ticker - ticker symbol (may be needed for series naming)
* @param interval - bar interval (may be needed for parsing/validation)
* @return BarSeries or null if parsing fails
* @throws DataMappingException if mapping fails
*/
BarSeries mapToBarSeries(String rawData, String ticker, Duration interval)
throws DataMappingException;
/**
* Maps InputStream to BarSeries.
* Caller is responsible for closing the stream.
*
* @param inputStream - stream containing raw data
* @param ticker - ticker symbol
* @param interval - bar interval
* @return BarSeries or null if parsing fails
* @throws DataMappingException if mapping fails
*/
BarSeries mapToBarSeries(InputStream inputStream, String ticker, Duration interval)
throws DataMappingException;
/**
* Returns the format name this mapper handles (e.g., "YahooFinance", "Binance", "CSV", "BitstampCSV").
* Used for logging and error messages.
*
* @return format name
*/
String getFormatName();
}
Location: ta4j-examples/src/main/java/ta4jexamples/datasources/client/DataRetrievalException.java
package ta4jexamples.datasources.client;
/**
* Exception thrown when data retrieval fails.
*/
public class DataRetrievalException extends Exception {
// Standard exception constructors
}
Location: ta4j-examples/src/main/java/ta4jexamples/datasources/mapper/DataMappingException.java
package ta4jexamples.datasources.mapper;
/**
* Exception thrown when data mapping/parsing fails.
*/
public class DataMappingException extends Exception {
// Standard exception constructors
}
Location: ta4j-examples/src/main/java/ta4jexamples/datasources/client/FileSearchStrategy.java
package ta4jexamples.datasources.client;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
/**
* Strategy interface for file search patterns.
* Handles the logic of finding files based on ticker, interval, and date range.
*
* Different implementations can handle different naming conventions:
* - StandardPatternFileSearchStrategy: {ticker}-{interval}-{start}_{end}.csv
* - SourcePrefixFileSearchStrategy: {source}-{ticker}-{interval}-{start}_{end}.csv
* - ExchangePrefixFileSearchStrategy: {exchange}-{ticker}-{interval}-{start}_{end}.json
*/
public interface FileSearchStrategy {
/**
* Searches for files matching the given criteria.
*
* @param ticker - ticker symbol
* @param interval - bar interval
* @param start - start date/time
* @param end - end date/time
* @return list of matching file paths (as strings), empty if none found
*/
List<String> searchFiles(String ticker, Duration interval, Instant start, Instant end);
/**
* Gets the source name prefix (if any) used in file naming.
*
* @return source name prefix, or empty string if none
*/
String getSourcePrefix();
}
Location: ta4j-examples/src/main/java/ta4jexamples/datasources/client/CacheableDataRetrievalClient.java
package ta4jexamples.datasources.client;
/**
* Marker interface for data retrieval clients that support caching.
*
* Implementations should:
* - Check cache before making actual retrieval
* - Store successful retrievals in cache
* - Handle cache invalidation/expiration
* - Provide cache configuration options
*/
public interface CacheableDataRetrievalClient extends DataRetrievalClient {
/**
* Enables or disables caching.
*
* @param enabled - true to enable caching
*/
void setCachingEnabled(boolean enabled);
/**
* Checks if caching is enabled.
*
* @return true if caching is enabled
*/
boolean isCachingEnabled();
/**
* Clears the cache.
*/
void clearCache();
}
ta4j-examples/src/main/java/ta4jexamples/datasources/
├── BarSeriesDataSource.java (unchanged - domain interface)
├── client/
│ ├── DataRetrievalClient.java (new)
│ ├── DataRetrievalException.java (new)
│ ├── CacheableDataRetrievalClient.java (new)
│ ├── FileSearchStrategy.java (new)
│ ├── http/
│ │ ├── HttpDataRetrievalClient.java (new)
│ │ └── CachedHttpDataRetrievalClient.java (new - wraps HttpDataRetrievalClient)
│ └── file/
│ ├── FileDataRetrievalClient.java (new)
│ └── StandardPatternFileSearchStrategy.java (new)
│ └── SourcePrefixFileSearchStrategy.java (new)
│ └── ExchangePrefixFileSearchStrategy.java (new)
├── mapper/
│ ├── DataFormatMapper.java (new)
│ ├── DataMappingException.java (new)
│ ├── json/
│ │ ├── YahooFinanceJsonMapper.java (new)
│ │ ├── BinanceJsonMapper.java (new)
│ │ ├── CoinbaseJsonMapper.java (new)
│ │ └── AdaptiveJsonMapper.java (new - handles multiple formats)
│ └── csv/
│ ├── CsvMapper.java (new)
│ └── BitstampCsvMapper.java (new)
├── YahooFinanceHttpBarSeriesDataSource.java (refactored - composes client + mapper)
├── CsvFileBarSeriesDataSource.java (refactored - composes client + mapper)
├── JsonFileBarSeriesDataSource.java (refactored - composes client + mapper)
└── BitStampCsvTradesFileBarSeriesDataSource.java (refactored - composes client + mapper)
ta4j-examples/src/main/java/ta4jexamples/datasources/**.6cce8809.temp/responses){ticker}-{interval}-{start}_{end}.csv{source}-{ticker}-{interval}-{start}_{end}.csv{exchange}-{ticker}-{interval}-{start}_{end}.jsonGoal: Extract HTTP retrieval logic from YahooFinanceHttpBarSeriesDataSource
Steps:
DataRetrievalClient interfaceDataRetrievalException classHttpDataRetrievalClient class
YahooFinanceHttpBarSeriesDataSource.loadSeriesSingleRequest()YahooFinanceHttpBarSeriesDataSource to use HttpDataRetrievalClient
HttpDataRetrievalClient via constructorclient.retrieveData(url)HttpDataRetrievalClient instead of HttpClientWrapperFiles to Create:
client/DataRetrievalClient.javaclient/DataRetrievalException.javaclient/http/HttpDataRetrievalClient.javaFiles to Modify:
YahooFinanceHttpBarSeriesDataSource.javaYahooFinanceHttpBarSeriesDataSourceTest.javaTesting:
HttpDataRetrievalClient independentlyGoal: Extract JSON parsing logic from YahooFinanceHttpBarSeriesDataSource
Steps:
DataFormatMapper interfaceDataMappingException classYahooFinanceJsonMapper class
parseYahooFinanceResponse() logicYahooFinanceHttpBarSeriesDataSource to use YahooFinanceJsonMapper
YahooFinanceJsonMapper via constructormapper.mapToBarSeries(rawData, ticker, interval)Files to Create:
mapper/DataFormatMapper.javamapper/DataMappingException.javamapper/json/YahooFinanceJsonMapper.javaFiles to Modify:
YahooFinanceHttpBarSeriesDataSource.javaYahooFinanceHttpBarSeriesDataSourceTest.javaTesting:
YahooFinanceJsonMapper with sample JSON responsesGoal: Move caching logic into a decorator pattern
Steps:
CacheableDataRetrievalClient interfaceCachedHttpDataRetrievalClient class (decorator)
HttpDataRetrievalClientYahooFinanceHttpBarSeriesDataSourceYahooFinanceHttpBarSeriesDataSource to use CachedHttpDataRetrievalClient
Files to Create:
client/CacheableDataRetrievalClient.javaclient/http/CachedHttpDataRetrievalClient.javaFiles to Modify:
YahooFinanceHttpBarSeriesDataSource.javaYahooFinanceHttpBarSeriesDataSourceTest.javaTesting:
CachedHttpDataRetrievalClient independentlyGoal: Extract file I/O logic from file-based data sources
Steps:
FileSearchStrategy interfaceFileSearchStrategy implementations:
StandardPatternFileSearchStrategy (for CsvFileBarSeriesDataSource)SourcePrefixFileSearchStrategy (for BitStampCsvTradesFileBarSeriesDataSource)ExchangePrefixFileSearchStrategy (for JsonFileBarSeriesDataSource)FileDataRetrievalClient class
FileSearchStrategy to find filesFileDataRetrievalClient
CsvFileBarSeriesDataSource: Use FileDataRetrievalClient + StandardPatternFileSearchStrategyBitStampCsvTradesFileBarSeriesDataSource: Use FileDataRetrievalClient + SourcePrefixFileSearchStrategyJsonFileBarSeriesDataSource: Use FileDataRetrievalClient + ExchangePrefixFileSearchStrategyFiles to Create:
client/FileSearchStrategy.javaclient/file/FileDataRetrievalClient.javaclient/file/StandardPatternFileSearchStrategy.javaclient/file/SourcePrefixFileSearchStrategy.javaclient/file/ExchangePrefixFileSearchStrategy.javaFiles to Modify:
CsvFileBarSeriesDataSource.javaBitStampCsvTradesFileBarSeriesDataSource.javaJsonFileBarSeriesDataSource.javaTesting:
FileDataRetrievalClient independentlyFileSearchStrategy implementationGoal: Extract parsing logic from file-based data sources
Steps:
CsvMapper: Generic CSV parser (for CsvFileBarSeriesDataSource)BitstampCsvMapper: Bitstamp-specific CSV parser with trade aggregationBinanceJsonMapper: Binance JSON formatCoinbaseJsonMapper: Coinbase JSON formatAdaptiveJsonMapper: Wraps Binance/Coinbase mappers, auto-detects formatCsvFileBarSeriesDataSource: Use CsvMapperBitStampCsvTradesFileBarSeriesDataSource: Use BitstampCsvMapperJsonFileBarSeriesDataSource: Use AdaptiveJsonMapperFiles to Create:
mapper/csv/CsvMapper.javamapper/csv/BitstampCsvMapper.javamapper/json/BinanceJsonMapper.javamapper/json/CoinbaseJsonMapper.javamapper/json/AdaptiveJsonMapper.javaFiles to Modify:
CsvFileBarSeriesDataSource.javaBitStampCsvTradesFileBarSeriesDataSource.javaJsonFileBarSeriesDataSource.javaTesting:
// Build components
HttpDataRetrievalClient httpClient = new HttpDataRetrievalClient(httpClientWrapper);
CachedHttpDataRetrievalClient cachedClient = new CachedHttpDataRetrievalClient(
httpClient,
Paths.get("temp/responses"),
true // caching enabled
);
YahooFinanceJsonMapper mapper = new YahooFinanceJsonMapper();
// Compose data source
YahooFinanceHttpBarSeriesDataSource yahoo = new YahooFinanceHttpBarSeriesDataSource(
cachedClient,
mapper
);
// Use as before
BarSeries series = yahoo.loadSeries("AAPL", Duration.ofDays(1), start, end);
// Build components
FileSearchStrategy searchStrategy = new StandardPatternFileSearchStrategy();
FileDataRetrievalClient fileClient = new FileDataRetrievalClient(searchStrategy);
CsvMapper csvMapper = new CsvMapper();
// Compose data source
CsvFileBarSeriesDataSource csv = new CsvFileBarSeriesDataSource(fileClient, csvMapper);
// Use as before
BarSeries series = csv.loadSeries("AAPL", Duration.ofDays(1), start, end);
// Build components
FileSearchStrategy searchStrategy = new ExchangePrefixFileSearchStrategy(
Arrays.asList("Coinbase", "Binance")
);
FileDataRetrievalClient fileClient = new FileDataRetrievalClient(searchStrategy);
BinanceJsonMapper binanceMapper = new BinanceJsonMapper();
CoinbaseJsonMapper coinbaseMapper = new CoinbaseJsonMapper();
AdaptiveJsonMapper adaptiveMapper = new AdaptiveJsonMapper(
Arrays.asList(binanceMapper, coinbaseMapper)
);
// Compose data source
JsonFileBarSeriesDataSource json = new JsonFileBarSeriesDataSource(fileClient, adaptiveMapper);
// Use as before
BarSeries series = json.loadSeries("ETH-USD", Duration.ofDays(1), start, end);
// Test mapper independently
@Test
public void testYahooFinanceJsonMapper() {
YahooFinanceJsonMapper mapper = new YahooFinanceJsonMapper();
String testJson = "{...valid Yahoo Finance JSON...}";
BarSeries series = mapper.mapToBarSeries(testJson, "AAPL", Duration.ofDays(1));
assertNotNull(series);
assertEquals("AAPL", series.getName());
assertTrue(series.getBarCount() > 0);
}
// Test client independently
@Test
public void testHttpDataRetrievalClient() {
HttpClientWrapper mockHttp = mock(HttpClientWrapper.class);
when(mockHttp.send(...)).thenReturn(mockResponse);
HttpDataRetrievalClient client = new HttpDataRetrievalClient(mockHttp);
String data = client.retrieveData("https://api.example.com/data");
assertNotNull(data);
}
// Test data source with mocked components
@Test
public void testYahooFinanceWithMockedComponents() {
DataRetrievalClient mockClient = mock(DataRetrievalClient.class);
DataFormatMapper mockMapper = mock(DataFormatMapper.class);
when(mockClient.retrieveData(anyString())).thenReturn("{...json...}");
when(mockMapper.mapToBarSeries(anyString(), anyString(), any()))
.thenReturn(mockBarSeries);
YahooFinanceHttpBarSeriesDataSource source = new YahooFinanceHttpBarSeriesDataSource(
mockClient, mockMapper);
BarSeries series = source.loadSeries("AAPL", Duration.ofDays(1), start, end);
assertNotNull(series);
}
All existing BarSeriesDataSource implementations should maintain their current public constructors and methods. The refactoring should be internal only.
Example:
// Existing constructor should still work
YahooFinanceHttpBarSeriesDataSource yahoo = new YahooFinanceHttpBarSeriesDataSource(true);
// Internally, this creates:
// - HttpDataRetrievalClient
// - CachedHttpDataRetrievalClient (if caching enabled)
// - YahooFinanceJsonMapper
// - Composes them together
Users can continue using existing APIs. Advanced users can opt into the new composition model if they want more control.
DataRetrievalClient (specifically FileDataRetrievalClient with FileSearchStrategy)DataRetrievalClient (via CacheableDataRetrievalClient decorator)FileSearchStrategy interface with multiple implementationsFileDataRetrievalClient uses strategy patternCachedHttpDataRetrievalClient wraps HttpDataRetrievalClient)DataRetrievalException and DataMappingExceptionOnce refactoring is complete, consider: