goldenChat base source add

This commit is contained in:
aidev
2026-05-23 15:11:48 +09:00
commit a4ea7762b5
2081 changed files with 1155760 additions and 0 deletions
@@ -0,0 +1,461 @@
/*
* SPDX-License-Identifier: MIT
*/
package ta4jexamples.indicators;
import java.text.NumberFormat;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.ta4j.core.Bar;
import org.ta4j.core.BarBuilder;
import org.ta4j.core.BarSeries;
import org.ta4j.core.BaseBarSeriesBuilder;
import org.ta4j.core.Indicator;
import org.ta4j.core.indicators.CachedIndicator;
import org.ta4j.core.indicators.averages.SMAIndicator;
import org.ta4j.core.indicators.helpers.ClosePriceIndicator;
import org.ta4j.core.num.DoubleNumFactory;
import org.ta4j.core.num.Num;
import org.ta4j.core.num.NumFactory;
/**
* Benchmark focused on the performance characteristics of
* {@link CachedIndicator}.
*
* <p>
* This is intended for before/after comparisons across branches: run this
* benchmark on the feature branch, then checkout {@code master} and run it
* again.
*
* <p>
* Scenarios:
* <ul>
* <li>Bounded cache eviction (hot path for streaming/rolling windows)</li>
* <li>Concurrent cache-hit reads (contention on read-mostly workloads)</li>
* <li>Repeated reads of the last bar (common in live feeds where the last bar
* is queried frequently)</li>
* </ul>
*
* @since 0.22.0
*/
public class CachedIndicatorBenchmark {
private static final Logger LOG = LogManager.getLogger(CachedIndicatorBenchmark.class);
private static final int DEFAULT_THREADS = Math.max(8, Runtime.getRuntime().availableProcessors());
private static final int DEFAULT_BATCHES = 3;
private static final int DEFAULT_EVICTION_BAR_COUNT = 200_000;
private static final int DEFAULT_MAXIMUM_BAR_COUNT_HINT = 512;
private static final int DEFAULT_CACHE_HIT_READS_PER_THREAD = 1_000_000;
private static final int DEFAULT_LAST_BAR_READS = 1_000_000;
private static final int DEFAULT_LAST_BAR_SMA_PERIOD = 50;
public static void main(String[] args) throws Exception {
int threads = args.length > 0 ? Integer.parseInt(args[0]) : DEFAULT_THREADS;
int batches = args.length > 1 ? Integer.parseInt(args[1]) : DEFAULT_BATCHES;
int evictionBars = args.length > 2 ? Integer.parseInt(args[2]) : DEFAULT_EVICTION_BAR_COUNT;
int cacheHitsPerThread = args.length > 3 ? Integer.parseInt(args[3]) : DEFAULT_CACHE_HIT_READS_PER_THREAD;
int lastBarReads = args.length > 4 ? Integer.parseInt(args[4]) : DEFAULT_LAST_BAR_READS;
int maximumBarCountHint = args.length > 5 ? Integer.parseInt(args[5]) : DEFAULT_MAXIMUM_BAR_COUNT_HINT;
int lastBarSmaPeriod = args.length > 6 ? Integer.parseInt(args[6]) : DEFAULT_LAST_BAR_SMA_PERIOD;
new CachedIndicatorBenchmark().run(threads, batches, evictionBars, cacheHitsPerThread, lastBarReads,
maximumBarCountHint, lastBarSmaPeriod);
}
ScenarioResult runBoundedEvictionScenario(int barCount, int maximumBarCountHint) {
BarSeries series = buildSeries(barCount);
return benchmarkBoundedEviction(series, maximumBarCountHint);
}
ScenarioResult runConcurrentCacheHitsScenario(int barCount, int threads, int readsPerThread) {
BarSeries series = buildSeries(barCount);
return benchmarkConcurrentCacheHits(series, threads, readsPerThread);
}
ScenarioResult runLastBarHotReadsScenario(int barCount, int smaPeriod, int reads) {
BarSeries series = buildSeries(barCount);
return benchmarkLastBarHotReads(series, smaPeriod, reads);
}
private void run(int threads, int batches, int evictionBars, int cacheHitsPerThread, int lastBarReads,
int maximumBarCountHint, int lastBarSmaPeriod) throws Exception {
LOG.info(
"Starting CachedIndicator benchmark: threads={}, batches={}, evictionBars={}, cacheHitsPerThread={}, lastBarReads={}, maxBarCountHint={}, lastBarSmaPeriod={}",
threads, batches, formatLong(evictionBars), formatLong(cacheHitsPerThread), formatLong(lastBarReads),
formatLong(maximumBarCountHint), formatLong(lastBarSmaPeriod));
var evictionSeries = buildSeries(evictionBars);
var cacheHitSeries = buildSeries(Math.max(5_000, lastBarSmaPeriod + 2));
var lastBarSeries = buildSeries(Math.max(5_000, lastBarSmaPeriod + 2));
Map<String, ScenarioStats> statsByScenario = new HashMap<>();
for (int batch = 1; batch <= batches; batch++) {
runScenario("Bounded eviction (monotonic indices)", batch, statsByScenario,
() -> benchmarkBoundedEviction(evictionSeries, maximumBarCountHint));
runScenario("Concurrent cache hits (same index)", batch, statsByScenario,
() -> benchmarkConcurrentCacheHits(cacheHitSeries, threads, cacheHitsPerThread));
runScenario("Last bar hot reads (SMA)", batch, statsByScenario,
() -> benchmarkLastBarHotReads(lastBarSeries, lastBarSmaPeriod, lastBarReads));
}
logSummary(statsByScenario);
}
private void runScenario(String name, int batch, Map<String, ScenarioStats> statsByScenario,
Supplier<ScenarioResult> runner) throws Exception {
LOG.info("Batch {}: {}", batch, name);
ScenarioResult result = runner.get();
ScenarioStats stats = statsByScenario.computeIfAbsent(name, ignored -> new ScenarioStats());
stats.add(result);
LOG.info(" duration={} ms, ops={}, throughput={} ops/s, checksum={}", formatMillis(result.durationNanos),
formatLong(result.operations), formatDouble(result.throughputOpsPerSecond), result.checksum);
}
private void logSummary(Map<String, ScenarioStats> statsByScenario) {
LOG.info("CachedIndicator benchmark summary (averages across batches):");
for (Map.Entry<String, ScenarioStats> entry : statsByScenario.entrySet()) {
ScenarioStats stats = entry.getValue();
LOG.info(" {}: avgDuration={} ms, avgThroughput={} ops/s (runs={})", entry.getKey(),
formatMillis(stats.averageDurationNanos()), formatDouble(stats.averageThroughputOpsPerSecond()),
stats.runs);
}
}
private ScenarioResult benchmarkBoundedEviction(BarSeries baseSeries, int maximumBarCountHint) {
BarSeries series = new MaxBarCountHintSeries(baseSeries, maximumBarCountHint);
Indicator<Integer> indicator = new IndexIndicator(series);
int endIndex = series.getEndIndex();
// Warm-up the cache machinery a bit (avoid timing the "first use" path).
for (int i = 0; i < Math.min(endIndex, maximumBarCountHint + 16); i++) {
indicator.getValue(i);
}
long checksum = 0;
int operations = Math.max(0, endIndex);
long startNanos = System.nanoTime();
for (int i = 0; i < endIndex; i++) {
checksum += indicator.getValue(i);
}
long durationNanos = System.nanoTime() - startNanos;
requireTrue(endIndex >= 1, "Series must contain at least one bar");
requireEquals(Integer.valueOf(endIndex - 1), indicator.getValue(endIndex - 1), "Index indicator mismatch");
return new ScenarioResult(operations, durationNanos, checksum);
}
private ScenarioResult benchmarkConcurrentCacheHits(BarSeries baseSeries, int threads, int readsPerThread) {
BarSeries series = new MaxBarCountHintSeries(baseSeries, Integer.MAX_VALUE);
Indicator<Integer> indicator = new IndexIndicator(series);
int hitIndex = Math.max(0, series.getEndIndex() - 1);
indicator.getValue(hitIndex);
ExecutorService pool = Executors.newFixedThreadPool(threads);
try {
CountDownLatch ready = new CountDownLatch(threads);
CountDownLatch start = new CountDownLatch(1);
CountDownLatch done = new CountDownLatch(threads);
List<CompletableFuture<Long>> futures = new ArrayList<>(threads);
for (int i = 0; i < threads; i++) {
futures.add(CompletableFuture.supplyAsync(() -> {
ready.countDown();
try {
start.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return 0L;
}
long localChecksum = 0;
for (int j = 0; j < readsPerThread; j++) {
localChecksum += indicator.getValue(hitIndex);
}
done.countDown();
return localChecksum;
}, pool));
}
awaitLatch(ready, Duration.ofSeconds(30), "workers to become ready");
long startNanos = System.nanoTime();
start.countDown();
awaitLatch(done, Duration.ofMinutes(2), "workers to finish");
long durationNanos = System.nanoTime() - startNanos;
long checksum = 0;
for (CompletableFuture<Long> future : futures) {
checksum += future.join();
}
long operations = (long) threads * readsPerThread;
return new ScenarioResult(operations, durationNanos, checksum);
} finally {
pool.shutdown();
try {
pool.awaitTermination(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
private ScenarioResult benchmarkLastBarHotReads(BarSeries baseSeries, int smaPeriod, int reads) {
BarSeries series = new MaxBarCountHintSeries(baseSeries, Integer.MAX_VALUE);
ClosePriceIndicator closePrice = new ClosePriceIndicator(series);
SMAIndicator sma = new SMAIndicator(closePrice, smaPeriod);
int endIndex = series.getEndIndex();
// Warm up: compute once so cache-hit behavior is measured.
Num expected = sma.getValue(endIndex);
long checksum = 0;
long startNanos = System.nanoTime();
for (int i = 0; i < reads; i++) {
Num value = sma.getValue(endIndex);
checksum += value.hashCode();
}
long durationNanos = System.nanoTime() - startNanos;
requireEquals(expected, sma.getValue(endIndex), "Last bar SMA must remain stable across reads");
return new ScenarioResult(reads, durationNanos, checksum);
}
static BarSeries buildSeries(int barCount) {
var numFactory = DoubleNumFactory.getInstance();
BarSeries series = new BaseBarSeriesBuilder().withNumFactory(numFactory).build();
Duration timePeriod = Duration.ofDays(1);
Instant endTime = Instant.EPOCH;
for (int i = 0; i < barCount; i++) {
endTime = endTime.plus(timePeriod);
series.barBuilder()
.timePeriod(timePeriod)
.endTime(endTime)
.closePrice(i + 1d)
.openPrice(i + 1d)
.highPrice(i + 1d)
.lowPrice(i + 1d)
.volume(1d)
.add();
}
return series;
}
private static void awaitLatch(CountDownLatch latch, Duration timeout, String what) {
try {
boolean completed = latch.await(timeout.toMillis(), TimeUnit.MILLISECONDS);
requireTrue(completed, "Timed out waiting for " + what);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IllegalStateException("Interrupted while waiting for " + what, e);
}
}
private static void requireTrue(boolean condition, String message) {
if (!condition) {
throw new IllegalStateException(message);
}
}
private static void requireEquals(Object expected, Object actual, String message) {
if (!Objects.equals(expected, actual)) {
throw new IllegalStateException(message + " (expected=" + expected + ", actual=" + actual + ')');
}
}
private static String formatLong(long value) {
return NumberFormat.getNumberInstance(Locale.US).format(value);
}
private static String formatMillis(double nanos) {
double millis = nanos / 1_000_000d;
return NumberFormat.getNumberInstance(Locale.US).format(millis);
}
private static String formatDouble(double value) {
NumberFormat format = NumberFormat.getNumberInstance(Locale.US);
format.setMaximumFractionDigits(2);
return format.format(value);
}
static final class ScenarioResult {
private final long operations;
private final long durationNanos;
private final long checksum;
private final double throughputOpsPerSecond;
ScenarioResult(long operations, long durationNanos, long checksum) {
this.operations = operations;
this.durationNanos = durationNanos;
this.checksum = checksum;
this.throughputOpsPerSecond = operations / (durationNanos / 1_000_000_000d);
}
long getOperations() {
return operations;
}
long getDurationNanos() {
return durationNanos;
}
long getChecksum() {
return checksum;
}
double getThroughputOpsPerSecond() {
return throughputOpsPerSecond;
}
}
private static final class ScenarioStats {
private int runs;
private double totalDurationNanos;
private double totalThroughputOpsPerSecond;
private void add(ScenarioResult result) {
runs++;
totalDurationNanos += result.durationNanos;
totalThroughputOpsPerSecond += result.throughputOpsPerSecond;
}
private double averageDurationNanos() {
return totalDurationNanos / runs;
}
private double averageThroughputOpsPerSecond() {
return totalThroughputOpsPerSecond / runs;
}
}
static final class IndexIndicator extends CachedIndicator<Integer> {
private IndexIndicator(BarSeries series) {
super(series);
}
@Override
protected Integer calculate(int index) {
return index;
}
@Override
public int getCountOfUnstableBars() {
return 0;
}
}
static final class MaxBarCountHintSeries implements BarSeries {
private static final long serialVersionUID = 3793483697719901088L;
private final BarSeries delegate;
private final int maximumBarCountHint;
private MaxBarCountHintSeries(BarSeries delegate, int maximumBarCountHint) {
this.delegate = delegate;
this.maximumBarCountHint = maximumBarCountHint;
}
@Override
public NumFactory numFactory() {
return delegate.numFactory();
}
@Override
public BarBuilder barBuilder() {
return delegate.barBuilder();
}
@Override
public String getName() {
return delegate.getName();
}
@Override
public Bar getBar(int i) {
return delegate.getBar(i);
}
@Override
public int getBarCount() {
return delegate.getBarCount();
}
@Override
public List<Bar> getBarData() {
return delegate.getBarData();
}
@Override
public int getBeginIndex() {
return delegate.getBeginIndex();
}
@Override
public int getEndIndex() {
return delegate.getEndIndex();
}
@Override
public int getMaximumBarCount() {
return maximumBarCountHint;
}
@Override
public void setMaximumBarCount(int maximumBarCount) {
throw new UnsupportedOperationException("Maximum bar count is a hint-only override for benchmarking");
}
@Override
public int getRemovedBarsCount() {
return delegate.getRemovedBarsCount();
}
@Override
public void addBar(Bar bar, boolean replace) {
delegate.addBar(bar, replace);
}
@Override
public void addTrade(Num tradeVolume, Num tradePrice) {
delegate.addTrade(tradeVolume, tradePrice);
}
@Override
public void addPrice(Num price) {
delegate.addPrice(price);
}
@Override
public BarSeries getSubSeries(int startIndex, int endIndex) {
return delegate.getSubSeries(startIndex, endIndex);
}
}
}
@@ -0,0 +1,151 @@
/*
* SPDX-License-Identifier: MIT
*/
package ta4jexamples.indicators;
import java.awt.Color;
import java.awt.Dimension;
import java.util.Date;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.DatasetRenderingOrder;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.CandlestickRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.ui.ApplicationFrame;
import org.jfree.chart.ui.UIUtils;
import org.jfree.data.time.Second;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.DefaultHighLowDataset;
import org.jfree.data.xy.OHLCDataset;
import org.ta4j.core.Bar;
import org.ta4j.core.BarSeries;
import org.ta4j.core.indicators.helpers.ClosePriceIndicator;
import ta4jexamples.datasources.BitStampCsvTradesFileBarSeriesDataSource;
/**
* This class builds a traditional candlestick chart.
*/
public class CandlestickChart {
/**
* Builds a JFreeChart OHLC dataset from a ta4j bar series.
*
* @param series the bar series
* @return an Open-High-Low-Close dataset
*/
private static OHLCDataset createOHLCDataset(BarSeries series) {
final int nbBars = series.getBarCount();
Date[] dates = new Date[nbBars];
double[] opens = new double[nbBars];
double[] highs = new double[nbBars];
double[] lows = new double[nbBars];
double[] closes = new double[nbBars];
double[] volumes = new double[nbBars];
for (int i = 0; i < nbBars; i++) {
Bar bar = series.getBar(i);
dates[i] = new Date(bar.getEndTime().toEpochMilli());
opens[i] = bar.getOpenPrice().doubleValue();
highs[i] = bar.getHighPrice().doubleValue();
lows[i] = bar.getLowPrice().doubleValue();
closes[i] = bar.getClosePrice().doubleValue();
volumes[i] = bar.getVolume().doubleValue();
}
return new DefaultHighLowDataset("btc", dates, highs, lows, opens, closes, volumes);
}
/**
* Builds an additional JFreeChart dataset from a ta4j bar series.
*
* @param series the bar series
* @return an additional dataset
*/
private static TimeSeriesCollection createAdditionalDataset(BarSeries series) {
ClosePriceIndicator indicator = new ClosePriceIndicator(series);
TimeSeriesCollection dataset = new TimeSeriesCollection();
TimeSeries chartTimeSeries = new TimeSeries("Btc price");
for (int i = 0; i < series.getBarCount(); i++) {
Bar bar = series.getBar(i);
chartTimeSeries.add(new Second(new Date(bar.getEndTime().toEpochMilli())),
indicator.getValue(i).doubleValue());
}
dataset.addSeries(chartTimeSeries);
return dataset;
}
/**
* Displays a chart in a frame. The frame is configured to avoid stealing focus
* when launched from tests or scripts.
*
* @param chart the chart to be displayed
*/
private static void displayChart(JFreeChart chart) {
// Chart panel
ChartPanel panel = new ChartPanel(chart);
panel.setFillZoomRectangle(true);
panel.setMouseWheelEnabled(true);
panel.setPreferredSize(new Dimension(740, 300));
// Application frame
ApplicationFrame frame = new ApplicationFrame("Ta4j example - Candlestick chart");
frame.setContentPane(panel);
frame.pack();
UIUtils.centerFrameOnScreen(frame);
frame.setFocusableWindowState(false);
frame.setVisible(true);
frame.setAlwaysOnTop(false);
frame.setAutoRequestFocus(false);
}
public static void main(String[] args) {
/*
* Getting bar series
*/
BarSeries series = BitStampCsvTradesFileBarSeriesDataSource.loadBitstampSeries();
/*
* Creating the OHLC dataset
*/
OHLCDataset ohlcDataset = createOHLCDataset(series);
/*
* Creating the additional dataset
*/
TimeSeriesCollection xyDataset = createAdditionalDataset(series);
/*
* Creating the chart
*/
JFreeChart chart = ChartFactory.createCandlestickChart("Bitstamp BTC price", "Time", "USD", ohlcDataset, true);
// Candlestick rendering
CandlestickRenderer renderer = new CandlestickRenderer();
renderer.setAutoWidthMethod(CandlestickRenderer.WIDTHMETHOD_SMALLEST);
XYPlot plot = chart.getXYPlot();
plot.setRenderer(renderer);
// Additional dataset
int index = 1;
plot.setDataset(index, xyDataset);
plot.mapDatasetToRangeAxis(index, 0);
XYLineAndShapeRenderer renderer2 = new XYLineAndShapeRenderer(true, false);
renderer2.setSeriesPaint(index, Color.blue);
plot.setRenderer(index, renderer2);
// Misc
plot.setRangeGridlinePaint(Color.lightGray);
plot.setBackgroundPaint(Color.white);
NumberAxis numberAxis = (NumberAxis) plot.getRangeAxis();
numberAxis.setAutoRangeIncludesZero(false);
plot.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);
/*
* Displaying the chart
*/
displayChart(chart);
}
}
@@ -0,0 +1,226 @@
/*
* SPDX-License-Identifier: MIT
*/
package ta4jexamples.indicators;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Stroke;
import java.util.Date;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.XYLineAnnotation;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.DatasetRenderingOrder;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.CandlestickRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.ui.ApplicationFrame;
import org.jfree.chart.ui.UIUtils;
import org.jfree.data.time.Second;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.DefaultHighLowDataset;
import org.jfree.data.xy.OHLCDataset;
import org.jfree.data.xy.XYDataset;
import org.ta4j.core.Bar;
import org.ta4j.core.BarSeries;
import org.ta4j.core.indicators.ChopIndicator;
import org.ta4j.core.indicators.helpers.ClosePriceIndicator;
import ta4jexamples.datasources.BitStampCsvTradesFileBarSeriesDataSource;
/**
* This class builds a traditional candlestick chart.
*/
public class CandlestickChartWithChopIndicator {
private static final int CHOP_INDICATOR_TIMEFRAME = 14;
private static final double CHOP_UPPER_THRESHOLD = 61.8;
private static final double CHOP_LOWER_THRESHOLD = 38.2;
private static final int VOLUME_DATASET_INDEX = 1;
private static final int CHOP_SCALE_VALUE = 100;
private static CombinedDomainXYPlot combinedPlot;
private static JFreeChart combinedChart;
static DateAxis xAxis = new DateAxis("Time");
private static ChartPanel combinedChartPanel;
private static XYPlot indicatorXYPlot;
static Stroke dashedThinLineStyle = new BasicStroke(0.4f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 1.0f,
new float[] { 8.0f, 4.0f }, 0.0f);
static BarSeries series;
/**
* Builds a JFreeChart OHLC dataset from a ta4j bar series.
*
* @param series a bar series
* @return an Open-High-Low-Close dataset
*/
private static OHLCDataset createOHLCDataset(BarSeries series) {
final int nbBars = series.getBarCount();
Date[] dates = new Date[nbBars];
double[] opens = new double[nbBars];
double[] highs = new double[nbBars];
double[] lows = new double[nbBars];
double[] closes = new double[nbBars];
double[] volumes = new double[nbBars];
for (int i = 0; i < nbBars; i++) {
Bar bar = series.getBar(i);
dates[i] = new Date(bar.getEndTime().toEpochMilli());
opens[i] = bar.getOpenPrice().doubleValue();
highs[i] = bar.getHighPrice().doubleValue();
lows[i] = bar.getLowPrice().doubleValue();
closes[i] = bar.getClosePrice().doubleValue();
volumes[i] = bar.getVolume().doubleValue();
}
return new DefaultHighLowDataset("btc", dates, highs, lows, opens, closes, volumes);
}
/**
* Builds an additional JFreeChart dataset from a ta4j bar series.
*
* @param series a bar series
* @return an additional dataset
*/
private static TimeSeriesCollection createAdditionalDataset(BarSeries series) {
ClosePriceIndicator indicator = new ClosePriceIndicator(series);
TimeSeriesCollection dataset = new TimeSeriesCollection();
TimeSeries chartTimeSeries = new TimeSeries("Btc price");
for (int i = 0; i < series.getBarCount(); i++) {
Bar bar = series.getBar(i);
chartTimeSeries.add(new Second(new Date(bar.getEndTime().toEpochMilli())),
indicator.getValue(i).doubleValue());
}
dataset.addSeries(chartTimeSeries);
return dataset;
}
private static TimeSeriesCollection createChopDataset(BarSeries series) {
ChopIndicator indicator = new ChopIndicator(series, CHOP_INDICATOR_TIMEFRAME, CHOP_SCALE_VALUE);
TimeSeriesCollection dataset = new TimeSeriesCollection();
TimeSeries chartTimeSeries = new TimeSeries("CHOP_14");
for (int i = 0; i < series.getBarCount(); i++) {
Bar bar = series.getBar(i);
if (i < CHOP_INDICATOR_TIMEFRAME)
continue;
chartTimeSeries.add(new Second(new Date(bar.getEndTime().toEpochMilli())),
indicator.getValue(i).doubleValue());
}
dataset.addSeries(chartTimeSeries);
return dataset;
}
/**
* Displays a chart in a frame. The frame is configured to avoid stealing focus
* when launched from tests or scripts.
*
* @param ohlcDataset
* @param xyDataset
* @param chopSeries
*/
private static void displayChart(XYDataset ohlcDataset, XYDataset xyDataset, XYDataset chopSeries) {
/*
* Create the chart
*/
CandlestickRenderer renderer = new CandlestickRenderer();
XYPlot pricePlot = new XYPlot(ohlcDataset, xAxis, new NumberAxis("Price"), renderer);
renderer.setAutoWidthMethod(CandlestickRenderer.WIDTHMETHOD_SMALLEST);
// volume dataset
pricePlot.setDataset(VOLUME_DATASET_INDEX, xyDataset);
pricePlot.mapDatasetToRangeAxis(VOLUME_DATASET_INDEX, 0);
// plot.setDomainAxis( xAxis );
XYLineAndShapeRenderer renderer2 = new XYLineAndShapeRenderer(true, false);
renderer2.setSeriesPaint(VOLUME_DATASET_INDEX, Color.blue);
pricePlot.setRenderer(VOLUME_DATASET_INDEX, renderer2);
// Misc
pricePlot.setRangeGridlinePaint(Color.lightGray);
pricePlot.setBackgroundPaint(Color.white);
NumberAxis numberAxis = (NumberAxis) pricePlot.getRangeAxis();
pricePlot.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);
renderer.setAutoWidthMethod(CandlestickRenderer.WIDTHMETHOD_SMALLEST);
// Misc
pricePlot.setRangeGridlinePaint(Color.lightGray);
pricePlot.setBackgroundPaint(Color.white);
numberAxis.setAutoRangeIncludesZero(false);
pricePlot.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);
// secondary study plot
indicatorXYPlot = new XYPlot( /* null, xAxis, yAxis, renderer */);
indicatorXYPlot.setDataset(chopSeries);
indicatorXYPlot.setRangeAxis(0, new NumberAxis(""));
indicatorXYPlot.setRenderer(0, new XYLineAndShapeRenderer());
NumberAxis yIndicatorAxis = new NumberAxis("");
yIndicatorAxis.setRange(0, CHOP_SCALE_VALUE);
indicatorXYPlot.setRangeAxis(0, yIndicatorAxis);
// combinedPlot
combinedPlot = new CombinedDomainXYPlot(xAxis); // DateAxis
combinedPlot.setGap(10.0);
// combinedPlot.setDomainAxis( xAxis );
combinedPlot.setBackgroundPaint(Color.LIGHT_GRAY);
combinedPlot.setDomainGridlinePaint(Color.GRAY);
combinedPlot.setRangeGridlinePaint(Color.GRAY);
combinedPlot.setOrientation(PlotOrientation.VERTICAL);
combinedPlot.add(pricePlot, 70);
combinedPlot.add(indicatorXYPlot, 30);
// Now create the chart that contains the combinedPlot
combinedChart = new JFreeChart("Bitstamp BTC price with Chop indicator", null, combinedPlot, true);
combinedChart.setBackgroundPaint(Color.LIGHT_GRAY);
// combinedChartPanel to contain combinedChart
combinedChartPanel = new ChartPanel(combinedChart);
combinedChartPanel.setLayout(new GridLayout(0, 1));
combinedChartPanel.setBackground(Color.LIGHT_GRAY);
combinedChartPanel.setPreferredSize(new Dimension(740, 300));
// Application frame
ApplicationFrame frame = new ApplicationFrame("Ta4j example - Candlestick chart");
frame.setContentPane(combinedChartPanel);
frame.pack();
UIUtils.centerFrameOnScreen(frame);
frame.setFocusableWindowState(false);
frame.setVisible(true);
frame.setAlwaysOnTop(false);
frame.setAutoRequestFocus(false);
// CHOP oscillator upper/lower threshold guidelines
XYLineAnnotation lineAnnotation = new XYLineAnnotation(
(double) series.getFirstBar().getBeginTime().toEpochMilli(), CHOP_LOWER_THRESHOLD,
(double) series.getLastBar().getEndTime().toEpochMilli(), CHOP_LOWER_THRESHOLD, dashedThinLineStyle,
Color.GREEN);
lineAnnotation.setToolTipText("tradable below this");
indicatorXYPlot.addAnnotation(lineAnnotation);
lineAnnotation = new XYLineAnnotation((double) series.getFirstBar().getBeginTime().toEpochMilli(),
CHOP_UPPER_THRESHOLD, (double) series.getLastBar().getEndTime().toEpochMilli(), CHOP_UPPER_THRESHOLD,
dashedThinLineStyle, Color.RED);
lineAnnotation.setToolTipText("too choppy above this");
indicatorXYPlot.addAnnotation(lineAnnotation);
}
public static void main(String[] args) {
series = BitStampCsvTradesFileBarSeriesDataSource.loadBitstampSeries();
/*
* Create the OHLC dataset from the data series
*/
OHLCDataset ohlcDataset = createOHLCDataset(series);
/*
* Create volume dataset
*/
TimeSeriesCollection xyDataset = createAdditionalDataset(series);
/*
* add the CHOP Indicator
*/
TimeSeriesCollection chopSeries = createChopDataset(series);
/*
* Display the chart
*/
displayChart(ohlcDataset, xyDataset, chopSeries);
}
}
@@ -0,0 +1,127 @@
/*
* SPDX-License-Identifier: MIT
*/
package ta4jexamples.indicators;
import java.awt.Dimension;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.ui.ApplicationFrame;
import org.jfree.chart.ui.UIUtils;
import org.jfree.data.time.Day;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.ta4j.core.Bar;
import org.ta4j.core.BarSeries;
import org.ta4j.core.Indicator;
import org.ta4j.core.indicators.averages.EMAIndicator;
import org.ta4j.core.indicators.bollinger.BollingerBandsLowerIndicator;
import org.ta4j.core.indicators.bollinger.BollingerBandsMiddleIndicator;
import org.ta4j.core.indicators.bollinger.BollingerBandsUpperIndicator;
import org.ta4j.core.indicators.helpers.ClosePriceIndicator;
import org.ta4j.core.indicators.statistics.StandardDeviationIndicator;
import org.ta4j.core.num.Num;
import ta4jexamples.datasources.CsvFileBarSeriesDataSource;
/**
* This class builds a graphical chart showing values from indicators.
*/
public class IndicatorsToChart {
/**
* Builds a JFreeChart time series from a Ta4j bar series and an indicator.
*
* @param barSeries the ta4j bar series
* @param indicator the indicator
* @param name the name of the chart time series
* @return the JFreeChart time series
*/
private static TimeSeries buildChartBarSeries(BarSeries barSeries, Indicator<Num> indicator, String name) {
TimeSeries chartTimeSeries = new TimeSeries(name);
for (int i = 0; i < barSeries.getBarCount(); i++) {
Bar bar = barSeries.getBar(i);
chartTimeSeries.add(new Day(Date.from(bar.getEndTime())), indicator.getValue(i).doubleValue());
}
return chartTimeSeries;
}
/**
* Displays a chart in a frame. The frame is configured to avoid stealing focus
* when launched from tests or scripts.
*
* @param chart the chart to be displayed
*/
private static void displayChart(JFreeChart chart) {
// Chart panel
ChartPanel panel = new ChartPanel(chart);
panel.setFillZoomRectangle(true);
panel.setMouseWheelEnabled(true);
panel.setPreferredSize(new Dimension(500, 270));
// Application frame
ApplicationFrame frame = new ApplicationFrame("Ta4j example - Indicators to chart");
frame.setContentPane(panel);
frame.pack();
UIUtils.centerFrameOnScreen(frame);
frame.setFocusableWindowState(false);
frame.setVisible(true);
frame.setAlwaysOnTop(false);
frame.setAutoRequestFocus(false);
}
public static void main(String[] args) {
/*
* Getting bar series
*/
BarSeries series = CsvFileBarSeriesDataSource.loadSeriesFromFile();
/*
* Creating indicators
*/
// Close price
ClosePriceIndicator closePrice = new ClosePriceIndicator(series);
EMAIndicator avg14 = new EMAIndicator(closePrice, 14);
StandardDeviationIndicator sd14 = new StandardDeviationIndicator(closePrice, 14);
// Bollinger bands
BollingerBandsMiddleIndicator middleBBand = new BollingerBandsMiddleIndicator(avg14);
BollingerBandsLowerIndicator lowBBand = new BollingerBandsLowerIndicator(middleBBand, sd14);
BollingerBandsUpperIndicator upBBand = new BollingerBandsUpperIndicator(middleBBand, sd14);
/*
* Building chart dataset
*/
TimeSeriesCollection dataset = new TimeSeriesCollection();
dataset.addSeries(buildChartBarSeries(series, closePrice, "Apple Inc. (AAPL) - NASDAQ GS"));
dataset.addSeries(buildChartBarSeries(series, lowBBand, "Low Bollinger Band"));
dataset.addSeries(buildChartBarSeries(series, upBBand, "High Bollinger Band"));
/*
* Creating the chart
*/
JFreeChart chart = ChartFactory.createTimeSeriesChart("Apple Inc. 2013 Close Prices", // title
"Date", // x-axis label
"Price Per Unit", // y-axis label
dataset, // data
true, // create legend?
true, // generate tooltips?
false // generate URLs?
);
XYPlot plot = (XYPlot) chart.getPlot();
DateAxis axis = (DateAxis) plot.getDomainAxis();
axis.setDateFormatOverride(new SimpleDateFormat("yyyy-MM-dd"));
/*
* Displaying the chart
*/
displayChart(chart);
}
}
@@ -0,0 +1,131 @@
/*
* SPDX-License-Identifier: MIT
*/
package ta4jexamples.indicators;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.ta4j.core.BarSeries;
import org.ta4j.core.indicators.ATRIndicator;
import org.ta4j.core.indicators.PPOIndicator;
import org.ta4j.core.indicators.ROCIndicator;
import org.ta4j.core.indicators.RSIIndicator;
import org.ta4j.core.indicators.WilliamsRIndicator;
import org.ta4j.core.indicators.averages.EMAIndicator;
import org.ta4j.core.indicators.averages.SMAIndicator;
import org.ta4j.core.indicators.helpers.ClosePriceIndicator;
import org.ta4j.core.indicators.helpers.ClosePriceRatioIndicator;
import org.ta4j.core.indicators.helpers.TypicalPriceIndicator;
import org.ta4j.core.indicators.statistics.StandardDeviationIndicator;
import ta4jexamples.datasources.BitStampCsvTradesFileBarSeriesDataSource;
/**
* This class builds a CSV file containing values from indicators.
*/
public class IndicatorsToCsv {
private static final Logger LOG = LogManager.getLogger(IndicatorsToCsv.class);
public static void main(String[] args) {
/*
* Getting bar series
*/
BarSeries series = BitStampCsvTradesFileBarSeriesDataSource.loadBitstampSeries();
/*
* Creating indicators
*/
// Close price
ClosePriceIndicator closePrice = new ClosePriceIndicator(series);
// Typical price
TypicalPriceIndicator typicalPrice = new TypicalPriceIndicator(series);
// Price variation
ClosePriceRatioIndicator closePriceRatioIndicator = new ClosePriceRatioIndicator(series);
// Simple moving averages
SMAIndicator shortSma = new SMAIndicator(closePrice, 8);
SMAIndicator longSma = new SMAIndicator(closePrice, 20);
// Exponential moving averages
EMAIndicator shortEma = new EMAIndicator(closePrice, 8);
EMAIndicator longEma = new EMAIndicator(closePrice, 20);
// Percentage price oscillator
PPOIndicator ppo = new PPOIndicator(closePrice, 12, 26);
// Rate of change
ROCIndicator roc = new ROCIndicator(closePrice, 100);
// Relative strength index
RSIIndicator rsi = new RSIIndicator(closePrice, 14);
// Williams %R
WilliamsRIndicator williamsR = new WilliamsRIndicator(series, 20);
// Average true range
ATRIndicator atr = new ATRIndicator(series, 20);
// Standard deviation
StandardDeviationIndicator sd = new StandardDeviationIndicator(closePrice, 14);
/*
* Building header
*/
StringBuilder sb = new StringBuilder(
"timestamp,close,typical,variation,sma8,sma20,ema8,ema20,ppo,roc,rsi,williamsr,atr,sd\n");
/*
* Adding indicators values
*/
final int nbBars = series.getBarCount();
for (int i = 0; i < nbBars; i++) {
sb.append(series.getBar(i).getEndTime())
.append(',')
.append(closePrice.getValue(i))
.append(',')
.append(typicalPrice.getValue(i))
.append(',')
.append(closePriceRatioIndicator.getValue(i))
.append(',')
.append(shortSma.getValue(i))
.append(',')
.append(longSma.getValue(i))
.append(',')
.append(shortEma.getValue(i))
.append(',')
.append(longEma.getValue(i))
.append(',')
.append(ppo.getValue(i))
.append(',')
.append(roc.getValue(i))
.append(',')
.append(rsi.getValue(i))
.append(',')
.append(williamsR.getValue(i))
.append(',')
.append(atr.getValue(i))
.append(',')
.append(sd.getValue(i))
.append('\n');
}
/*
* Writing CSV file
*/
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter(new File("target", "indicators.csv")));
writer.write(sb.toString());
} catch (IOException ioe) {
LOG.error("Unable to write CSV file", ioe);
} finally {
try {
if (writer != null) {
writer.close();
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
}