goldenChat base source add
This commit is contained in:
+250
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
package ta4jexamples.datasources;
|
||||
|
||||
import com.opencsv.CSVParserBuilder;
|
||||
import com.opencsv.CSVReader;
|
||||
import com.opencsv.CSVReaderBuilder;
|
||||
import com.opencsv.exceptions.CsvValidationException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.ta4j.core.BarSeries;
|
||||
import org.ta4j.core.BaseBarSeriesBuilder;
|
||||
import ta4jexamples.datasources.file.AbstractFileBarSeriesDataSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* This class builds a Ta4j bar series from a CSV file containing bars.
|
||||
* <p>
|
||||
* Implements {@link BarSeriesDataSource} to support domain-driven loading by
|
||||
* ticker, interval, and date range. Searches for CSV files matching the
|
||||
* specified criteria in the classpath.
|
||||
*/
|
||||
public class CsvFileBarSeriesDataSource extends AbstractFileBarSeriesDataSource {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(CsvFileBarSeriesDataSource.class);
|
||||
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
private static final String DEFAULT_APPLE_BAR_FILE = "AAPL-PT1D-20130102_20131231.csv";
|
||||
|
||||
/**
|
||||
* Creates a new CsvFileBarSeriesDataSource with no source prefix.
|
||||
*/
|
||||
public CsvFileBarSeriesDataSource() {
|
||||
super("");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFileExtension() {
|
||||
return "csv";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BarSeries searchAndLoadFile(String ticker, String intervalStr, String sourcePrefix,
|
||||
String startDateTimeStr, String endDateTimeStr, String startDateStr, String endDateStr, Duration interval,
|
||||
Instant start, Instant end) {
|
||||
// Try exact pattern with interval-appropriate format:
|
||||
// {ticker}-{interval}-{startDateTime}_{endDateTime}.csv
|
||||
String exactPattern = ticker.toUpperCase() + "-" + intervalStr + "-" + startDateTimeStr + "_" + endDateTimeStr
|
||||
+ ".csv";
|
||||
BarSeries series = loadCsvSeries(exactPattern);
|
||||
if (series != null && !series.isEmpty()) {
|
||||
return series;
|
||||
}
|
||||
|
||||
// Fallback to date-only format for existing files
|
||||
String exactPatternDateOnly = ticker.toUpperCase() + "-" + intervalStr + "-" + startDateStr + "_" + endDateStr
|
||||
+ ".csv";
|
||||
series = loadCsvSeries(exactPatternDateOnly);
|
||||
if (series != null && !series.isEmpty()) {
|
||||
return series;
|
||||
}
|
||||
|
||||
// Try broader pattern: {ticker}-*-{startDateTime}_*.csv
|
||||
String broaderPattern = ticker.toUpperCase() + "-*-" + startDateTimeStr + "_*.csv";
|
||||
series = searchAndLoadCsvFile(broaderPattern, start, end);
|
||||
if (series != null && !series.isEmpty()) {
|
||||
return series;
|
||||
}
|
||||
|
||||
// Fallback to date-only format for broader pattern
|
||||
String broaderPatternDateOnly = ticker.toUpperCase() + "-*-" + startDateStr + "_*.csv";
|
||||
series = searchAndLoadCsvFile(broaderPatternDateOnly, start, end);
|
||||
if (series != null && !series.isEmpty()) {
|
||||
return series;
|
||||
}
|
||||
|
||||
// Try even broader: {ticker}-*.csv (then filter by date range)
|
||||
String broadestPattern = ticker.toUpperCase() + "-*.csv";
|
||||
series = searchAndLoadCsvFile(broadestPattern, start, end);
|
||||
if (series != null && !series.isEmpty()) {
|
||||
return filterSeriesByDateRange(series, start, end);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BarSeries loadSeries(String source) {
|
||||
if (source == null || source.trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("Source cannot be null or empty");
|
||||
}
|
||||
return loadCsvSeries(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a CSV file matching the pattern and loads it if found.
|
||||
*
|
||||
* @param pattern the filename pattern to search for (supports wildcards)
|
||||
* @param start the start date (for validation)
|
||||
* @param end the end date (for validation)
|
||||
* @return the loaded BarSeries, or null if not found
|
||||
*/
|
||||
private BarSeries searchAndLoadCsvFile(String pattern, Instant start, Instant end) {
|
||||
// Try direct pattern match as resource
|
||||
if (!pattern.contains("*")) {
|
||||
return loadCsvSeries(pattern);
|
||||
}
|
||||
|
||||
// Count wildcards to determine replacement strategy
|
||||
long wildcardCount = pattern.chars().filter(ch -> ch == '*').count();
|
||||
|
||||
if (wildcardCount == 1) {
|
||||
// Single wildcard: try common interval values
|
||||
String[] intervalVariations = { "PT1D", "PT5M", "PT1H", "" };
|
||||
for (String interval : intervalVariations) {
|
||||
String variation = pattern.replaceFirst("\\*", interval);
|
||||
BarSeries series = loadCsvSeries(variation);
|
||||
if (series != null && !series.isEmpty()) {
|
||||
return series;
|
||||
}
|
||||
}
|
||||
} else if (wildcardCount >= 2) {
|
||||
// Multiple wildcards: replace the first (interval) with common values,
|
||||
// and the second (end date) with the actual end date from parameters
|
||||
String[] intervalVariations = { "PT1D", "PT5M", "PT1H" };
|
||||
// Format end date in both date-only and datetime formats
|
||||
String endDateStr = end.atZone(ZoneOffset.UTC).format(FILENAME_DATE_FORMAT);
|
||||
String endDateTimeStr = end.atZone(ZoneOffset.UTC)
|
||||
.format(getDateTimeFormatterForInterval(Duration.ofDays(1)));
|
||||
|
||||
for (String interval : intervalVariations) {
|
||||
// Replace first wildcard with interval, second with end date (date-only format)
|
||||
String variation = pattern.replaceFirst("\\*", interval).replaceFirst("\\*", endDateStr);
|
||||
BarSeries series = loadCsvSeries(variation);
|
||||
if (series != null && !series.isEmpty()) {
|
||||
return series;
|
||||
}
|
||||
// Also try with datetime format for the end date
|
||||
variation = pattern.replaceFirst("\\*", interval).replaceFirst("\\*", endDateTimeStr);
|
||||
series = loadCsvSeries(variation);
|
||||
if (series != null && !series.isEmpty()) {
|
||||
return series;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a bar series from the default CSV file.
|
||||
*
|
||||
* @return the bar series loaded from the default CSV file
|
||||
*/
|
||||
public static BarSeries loadSeriesFromFile() {
|
||||
return loadSeriesFromFile(DEFAULT_APPLE_BAR_FILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a bar series from the specified CSV file. This is a convenience method
|
||||
* that delegates to {@link #loadCsvSeries(String)}.
|
||||
*
|
||||
* @param csvFile the path to the CSV file containing bar data
|
||||
* @return the bar series loaded from the specified CSV file
|
||||
*/
|
||||
public static BarSeries loadSeriesFromFile(String csvFile) {
|
||||
return loadCsvSeries(csvFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a bar series from a CSV file with the specified filename. The CSV file
|
||||
* is expected to contain stock market data with the following columns: date,
|
||||
* open price, high price, low price, close price, and volume. The date format
|
||||
* is expected to match the predefined DATE_FORMAT.
|
||||
*
|
||||
* @param filename the name of the CSV file to load
|
||||
* @return the bar series containing stock data loaded from the specified CSV
|
||||
* file, or null if the file is not found or empty
|
||||
*/
|
||||
public static BarSeries loadCsvSeries(String filename) {
|
||||
|
||||
var stream = CsvFileBarSeriesDataSource.class.getClassLoader().getResourceAsStream(filename);
|
||||
|
||||
if (stream == null) {
|
||||
LOG.debug("CSV file not found in classpath: {}", filename);
|
||||
return null;
|
||||
}
|
||||
|
||||
var series = new BaseBarSeriesBuilder().withName(filename).build();
|
||||
|
||||
try (stream) {
|
||||
try (InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) {
|
||||
try (CSVReader csvReader = new CSVReaderBuilder(reader)
|
||||
.withCSVParser(new CSVParserBuilder().withSeparator(',').build())
|
||||
.withSkipLines(1)
|
||||
.build()) {
|
||||
String[] line;
|
||||
while ((line = csvReader.readNext()) != null) {
|
||||
Instant date = LocalDate.parse(line[0], DATE_FORMAT).atStartOfDay(ZoneOffset.UTC).toInstant();
|
||||
double open = Double.parseDouble(line[1]);
|
||||
double high = Double.parseDouble(line[2]);
|
||||
double low = Double.parseDouble(line[3]);
|
||||
double close = Double.parseDouble(line[4]);
|
||||
double volume = Double.parseDouble(line[5]);
|
||||
|
||||
series.barBuilder()
|
||||
.timePeriod(Duration.ofDays(1))
|
||||
.endTime(date)
|
||||
.openPrice(open)
|
||||
.closePrice(close)
|
||||
.highPrice(high)
|
||||
.lowPrice(low)
|
||||
.volume(volume)
|
||||
.amount(0)
|
||||
.add();
|
||||
}
|
||||
} catch (CsvValidationException e) {
|
||||
LOG.error("Unable to load bars from CSV. File is not valid csv.", e);
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
LOG.error("Unable to load bars from CSV", ioe);
|
||||
} catch (NumberFormatException nfe) {
|
||||
LOG.error("Error while parsing value", nfe);
|
||||
return null;
|
||||
}
|
||||
return series.isEmpty() ? null : series;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
BarSeries series = CsvFileBarSeriesDataSource.loadSeriesFromFile();
|
||||
|
||||
LOG.debug("Series: {} ({})", series.getName(), series.getSeriesPeriodDescription());
|
||||
LOG.debug("Number of bars: {}", series.getBarCount());
|
||||
if (series.isEmpty()) {
|
||||
LOG.warn("Series is empty - no bars loaded from CSV file. Skipping first bar details.");
|
||||
} else {
|
||||
LOG.debug("First bar: \n\tVolume: {}\n\tOpen price: {}\n\tClose price: {}", series.getBar(0).getVolume(),
|
||||
series.getBar(0).getOpenPrice(), series.getBar(0).getClosePrice());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user