/*
* Licensed Materials - Property of Rogue Wave Software, Inc.
* © Copyright Rogue Wave Software, Inc. 2014, 2017
* © Copyright IBM Corp. 2009, 2014
* © Copyright ILOG 1996, 2009
* All Rights Reserved.
*
* Note to U.S. Government Users Restricted Rights:
* The Software and Documentation were developed at private expense and
* are "Commercial Items" as that term is defined at 48 CFR 2.101,
* consisting of "Commercial Computer Software" and
* "Commercial Computer Software Documentation", as such terms are
* used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
* as applicable.
*/
package stock;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.net.URL;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JSplitPane;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import com.ibm.icu.text.DecimalFormat;
import com.ibm.icu.text.NumberFormat;
import ilog.views.chart.IlvAffineAxisTransformer;
import ilog.views.chart.IlvAxis;
import ilog.views.chart.IlvAxisTransformerException;
import ilog.views.chart.IlvChart;
import ilog.views.chart.IlvChartInteractor;
import ilog.views.chart.IlvChartRenderer;
import ilog.views.chart.IlvColor;
import ilog.views.chart.IlvDataInterval;
import ilog.views.chart.IlvDefaultStepsDefinition;
import ilog.views.chart.IlvDisplayPoint;
import ilog.views.chart.IlvGrid;
import ilog.views.chart.IlvLabelRenderer;
import ilog.views.chart.IlvLegend;
import ilog.views.chart.IlvLocalZoomAxisTransformer;
import ilog.views.chart.IlvScale;
import ilog.views.chart.IlvStyle;
import ilog.views.chart.IlvTimeUnit;
import ilog.views.chart.action.IlvChartAction;
import ilog.views.chart.action.IlvChartFitAction;
import ilog.views.chart.data.IlvDataSet;
import ilog.views.chart.data.IlvDefaultDataSet;
import ilog.views.chart.data.IlvDefaultDataSource;
import ilog.views.chart.event.AxisChangeEvent;
import ilog.views.chart.event.AxisListener;
import ilog.views.chart.event.AxisRangeEvent;
import ilog.views.chart.event.ChartHighlightInteractionEvent;
import ilog.views.chart.event.ChartInteractionEvent;
import ilog.views.chart.event.ChartInteractionListener;
import ilog.views.chart.event.DataSourceEvent;
import ilog.views.chart.event.DataSourceListener;
import ilog.views.chart.interactor.IlvChartLocalPanInteractor;
import ilog.views.chart.interactor.IlvChartLocalReshapeInteractor;
import ilog.views.chart.interactor.IlvChartPanInteractor;
import ilog.views.chart.interactor.IlvChartXScrollInteractor;
import ilog.views.chart.renderer.IlvHiLoChartRenderer;
import ilog.views.chart.renderer.IlvPolylineChartRenderer;
import ilog.views.chart.renderer.IlvSingleAreaRenderer;
import ilog.views.chart.renderer.IlvSingleBarRenderer;
import ilog.views.chart.renderer.IlvSinglePolylineRenderer;
import ilog.views.chart.renderer.IlvSingleStairRenderer;
import ilog.views.chart.util.IlvDoubleArray;
import ilog.views.util.IlvLocaleUtil;
import ilog.views.util.time.IlvCalendarFactory;
import shared.AbstractChartExample;
import stock.indicator.BollingerBandsIndicator;
import stock.indicator.MACDIndicator;
import stock.indicator.MovingAverageIndicator;
import stock.indicator.PriceChannelIndicator;
import stock.indicator.RSIIndicator;
import stock.indicator.StochasticIndicator;
import stock.indicator.TechnicalIndicator;
import stock.indicator.VolumeIndicator;
import stock.indicator.WilliamsRIndicator;
/**
* A simple application that displays stock information. The sample loads quotes
* from the Yahoo finance Web pages and displays them in two distinct charts on
* which you can perform interactions. The displayed information is the stock
* prices and the exchanged volume. The data is retrieved by sending a formatted
* <i>cgi query</i> to the Yahoo Web site, and the result is decoded using a
* custom <code>IlvDataReader</code> object. This reader (an instance of
* <code>CSVDataReader</code>) extracts the data from the input stream and
* initializes the data source with the corresponding data sets.
*/
public class StockDemo extends AbstractChartExample implements ActionListener, StockColors {
// private static final int MAIN_WINDOW_DEFAULT_WIDTH = 600;
private static final int MAIN_WINDOW_DEFAULT_HEIGHT = 700;
/** The number of lower charts. */
private static final int LOWER_CHART_COUNT = 2;
private static final int RIGHT_MARGIN = 6;
private static final String HIGHLIGHT_STATUS_KEY = "HIGHLIGHT_STATUS_KEY";
private static final Font DEFAULT_FONT = new Font("Dialog", Font.PLAIN, 10);
public static final int LINE = 0;
public static final int AREA = 1;
public static final int BAR = 2;
public static final int STAIR = 3;
public static final int HLOC = 4;
public static final int CANDLE = 5;
public static final String LINE_CMD = "Line_CMD";
public static final String AREA_CMD = "Area_CMD";
public static final String BAR_CMD = "Bar_CMD";
public static final String STAIR_CMD = "Stair_CMD";
public static final String HLOC_CMD = "HLOC_CMD";
public static final String CANDLE_CMD = "Candle_CMD";
public static final String LOCALZOOM_CMD = "LocalZoom_CMD";
public static final String THRESHOLD_CMD = "Threshold_CMD";
/**
* The threshold beyond which quotes are displayed with an overview
* representation mode. This constant is equal to the approximate duration of
* 3 months for daily data and one year for weekly data.
*/
public static final double OVERVIEW_THRESHOLD = 65;
static {
List<Color> colorList = IlvColor.getDefaultColors();
colorList.clear();
colorList.addAll(Arrays.asList(DEFAULT_COLORS));
}
// == Chart related data members
StockDataSource stockDS;
IlvChart mainChart;
IlvChart[] lowerCharts;
List<TechnicalIndicator> upperIndicators = new ArrayList<TechnicalIndicator>();
TechnicalIndicator[] lowerIndicators;
/** The overview renderer for the primary symbol. */
IlvChartRenderer overviewRenderer;
/** The detailed renderer for the primary symbol. */
IlvChartRenderer detailedRenderer;
/** The renderer for the seconary symbols. */
IlvChartRenderer secondaryRenderer;
/**
* Stores all the interactors that should be set on the upper chart. The first
* array contains the default interactors, while the second array holds the
* interactors when the local zoom is active.
*/
IlvChartInteractor[][] upperInteractors = new IlvChartInteractor[2][];
IlvChartInteractor[][][] lowerInteractors = new IlvChartInteractor[2][][];
AxisZoomHistory zoomHistory = new AxisZoomHistory();
Highlighter highlighter;
boolean antiAliasing = false;
// == UI related data members
private StockQueryPanel queryPanel;
private JButton refreshButton;
private JToggleButton thresholdButton;
private IlvChartAction backAction;
private IlvChartAction fwdAction;
private IlvChartAction fitAction;
private LocalZoomAction zoomInAction;
private LocalZoomAction zoomOutAction;
private List<IndicatorAction> upperIndicatorActions;
private List<IndicatorAction>[] lowerIndicatorActions;
private ThresholdLines threshold;
private ChartMessage loadingMessage;
private NumberFormat numFmt = new DecimalFormat(".###");
private DateFormat dateFmt = DateFormat.getDateInstance(DateFormat.SHORT);
private JFrame topFrame;
private JToolBar toolBar;
private QuoteDisplayPanel quoteDisplay;
private PercentDisplayer percentDisplayer;
/**
* Initializes a new <code>TableModelDemo</code> object.
**/
Override
public void init(Container container) {
super.init(container);
container.setLayout(new BorderLayout());
container.setBackground(BACKGROUND);
numFmt = NumberFormat.getNumberInstance();
numFmt.setMaximumFractionDigits(3);
// Create the stock data source to read the result of the query.
URL defaultDataURL = null;
try {
defaultDataURL = getClass().getResource("default.csv");
} catch (Exception x) {
x.printStackTrace();
}
stockDS = new StockDataSource(defaultDataURL);
// == Create the main chart.
mainChart = createMainChart();
percentDisplayer = new PercentDisplayer();
setOverviewRepresentation(LINE);
setDetailedRepresentation(CANDLE);
secondaryRenderer = new IlvPolylineChartRenderer();
mainChart.addRenderer(secondaryRenderer);
// == Create indicator charts.
lowerCharts = createLowerCharts();
lowerIndicators = new TechnicalIndicator[lowerCharts.length];
// == Create the interactors.
createInteractors();
// == Create and add the Query and Config panels.
queryPanel = new StockQueryPanel();
queryPanel.addActionListener(this);
JPanel top = new JPanel(new BorderLayout());
toolBar = createToolBar();
top.add(toolBar, BorderLayout.NORTH);
top.add(queryPanel, BorderLayout.CENTER);
container.add(top, BorderLayout.NORTH);
top.getRootPane().setDefaultButton((JButton) toolBar.getComponentAtIndex(0));
// Install interactors
installDefaultInteractors();
int count = lowerCharts.length;
JPanel lowerChartsPanel = new JPanel(new GridLayout(count, 1, 0, 4));
lowerChartsPanel.setOpaque(false);
lowerChartsPanel.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0));
for (int i = 0; i < count; ++i) {
lowerChartsPanel.add(lowerCharts[i]);
}
final JSplitPane center = new JSplitPane(JSplitPane.VERTICAL_SPLIT, mainChart, lowerChartsPanel);
center.setDividerLocation(MAIN_WINDOW_DEFAULT_HEIGHT / 2 - 50);
center.setDividerSize(2);
center.setOpaque(false);
center.setBorder(BorderFactory.createEmptyBorder(6, 0, 0, 0));
container.add(center, BorderLayout.CENTER);
// Create and install the menu bar.
setMenuBar(createMenuBar());
setAntiAliasing(true);
// Load the default data.
loadDefaultData();
}
/**
* Returns the data source that holds the stock data.
*/
public StockDataSource getStockDataSource() {
return stockDS;
}
// --------------------------------------------------------------------------
// - Indicators
/** Sets a lower indicator. */
protected void setLowerIndicator(int idx, TechnicalIndicator indicator) {
if (lowerIndicators[idx] == indicator)
return;
if (lowerIndicators[idx] != null)
lowerIndicators[idx].detach();
if (indicator != null)
indicator.attach(lowerCharts[idx]);
lowerIndicators[idx] = indicator;
}
/** Adds an upper indicator. */
protected void addUpperIndicator(TechnicalIndicator indicator) {
if (indicator != null)
indicator.attach(mainChart);
upperIndicators.add(indicator);
}
/** Removes an upper indicator. */
protected void removeUpperIndicator(TechnicalIndicator indicator) {
if (!upperIndicators.contains(indicator))
return;
if (indicator != null)
indicator.detach();
upperIndicators.remove(indicator);
}
/**
* Creates the highlight component. This component will be set in the header
* of the specified chart.
*/
protected JComponent createHighlightComponent(IlvChart chart, JComponent leftComponent, int highlightStatusWidth) {
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(4, 0, 4, RIGHT_MARGIN));
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
panel.setBackground(HEADER_COLOR);
panel.add(leftComponent);
JLabel status = new JLabel(" ", JLabel.RIGHT);
status.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 4));
status.setFont(DEFAULT_FONT);
Dimension size = status.getPreferredSize();
size.width = highlightStatusWidth;
status.setPreferredSize(size);
status.setMinimumSize(size);
status.setMaximumSize(size);
status.setForeground(FOREGROUND);
status.setBackground(BACKGROUND);
status.setOpaque(true);
chart.putClientProperty(HIGHLIGHT_STATUS_KEY, status);
panel.add(status);
return panel;
}
/**
* Creates the main chart. This chart displays the stock data and the upper
* indicators.
*/
protected IlvChart createMainChart() {
IlvChart chart = createChart();
chart.setBorder(BorderFactory.createEmptyBorder(0, 0, 4, 0));
chart.getXAxis().setVisibleRange(0, 10);
chart.getYScale(0).setTitle("Share price ($)", 270);
chart.getYScale(0).getTitleRenderer().setFont(new Font("Dialog", Font.PLAIN, 12));
Stripes stripes = new Stripes(chart.getYScale(0), STRIPES_COLOR);
stripes.setDrawOrder(-3);
chart.addDecoration(stripes);
chart.getYAxis(0).setVisibleRange(0, 100);
chart.getXScale().setMajorTickVisible(false);
chart.getXScale().setVisible(false);
// Add an axis listener to toggle the visibility of the
// quote renderers/quote overview.
chart.getXAxis().addAxisListener(new AxisListener() {
double oldZoomfactor;
double oldLen = 0;
boolean oldAntiAliasing;
Override
public void axisRangeChanged(AxisRangeEvent evt) {
if (evt.isChangedEvent() && !evt.isAdjusting() && evt.isVisibleRangeEvent()) {
updateDisplay(evt.getAxis());
} else if (evt.isVisibleRangeEvent() && !evt.isChangedEvent()) {
double min = evt.getNewMin();
double max = evt.getNewMax();
if (max - min < StockUtil.MIN_XLEN) {
double mid = (max + min) / 2;
evt.setNewMax(mid + StockUtil.MIN_XLEN / 2);
evt.setNewMin(mid - StockUtil.MIN_XLEN / 2);
}
}
}
Override
public void axisChanged(AxisChangeEvent evt) {
if (evt.getType() == AxisChangeEvent.ADJUSTMENT_CHANGE) {
if (evt.isAdjusting()) {
if (StockDemo.this.antiAliasing) {
StockDemo.this.setAntiAliasing(false);
oldAntiAliasing = true;
}
} else {
if (oldAntiAliasing) {
StockDemo.this.setAntiAliasing(true);
oldAntiAliasing = false;
}
updateDisplay(evt.getAxis());
}
} else if (evt.getType() == AxisChangeEvent.TRANSFORMER_CHANGE) {
LocalZoomHandler lzh = LocalZoomHandler.get(mainChart, -1);
if (lzh == null)
return;
if (oldZoomfactor != lzh.getTransformer().getZoomFactor()) {
zoomInAction.computeEnabled();
zoomOutAction.computeEnabled();
oldZoomfactor = lzh.getTransformer().getZoomFactor();
}
}
}
private void updateDisplay(IlvAxis axis) {
// Check if the zoom level has changed and call the
// xRangeChanged() method.
IlvDataInterval xRange = mainChart.getXAxis().getVisibleRange();
double newLen = xRange.getLength();
boolean zoom = (oldLen == 0) || (Math.abs(newLen - oldLen) / oldLen > .01);
StockDemo.this.xRangeChanged(zoom);
oldLen = newLen;
}
});
// == Add the quote display panel and the legend as a header.
JPanel header = new JPanel(new GridLayout(0, 1, 0, 4));
header.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 8));
header.setBackground(HEADER_COLOR);
quoteDisplay = new QuoteDisplayPanel();
header.add(quoteDisplay);
IlvLegend legend = createLegend();
chart.setLegend(legend);
header.add(legend);
chart.setHeader(header);
return chart;
}
/**
* Handles the display of percentage variation on a secondary y-scale.
*/
class PercentDisplayer implements AxisListener, DataSourceListener {
/** The transform that maps stock prices into percentage variation. */
IlvAffineAxisTransformer transform = new IlvAffineAxisTransformer(1., 0);
boolean active = false;
boolean visible = false;
IlvScale percentScale;
IlvGrid percentGrid;
PercentDisplayer() {
// Add an y-axis
IlvAxis yAxis = mainChart.addYAxis(false, false);
yAxis.setTransformer(transform);
yAxis.synchronizeWith(mainChart.getYAxis(0), true);
// A scale with custom label formatting
percentScale = new IlvScale() {
Override
public String computeLabel(double v) {
String res = super.computeLabel(v);
return (getPercentVariation(v) > 0) ? ('+' + res + '%') : (res + '%');
}
};
percentScale.setMajorTickVisible(false);
percentScale.setStepUnit(null, new Double(0));
// A grid that draws a single grid line at 0% variation.
percentGrid = new IlvGrid() {
Override
public void draw(Graphics g) {
if (getChart() == null)
return;
IlvAxis axis = getAxis();
if (axis.getTVisibleRange().isInside(0)) {
try {
IlvDoubleArray values = new IlvDoubleArray();
values.add(transform.inverse(0));
draw(g, values, true);
} catch (IlvAxisTransformerException x) {
}
}
}
};
Stroke stroke = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10.0f,
new float[] { 10.f, 6.f }, 0.0f);
percentGrid.setMajorStroke(stroke);
percentGrid.setMajorPaint(Color.white);
setActive(true);
}
/**
* Returns the percent variation corresponding to the specified stock price.
*/
public double getPercentVariation(double price) {
try {
return transform.apply(price);
} catch (IlvAxisTransformerException x) {
return 0;
}
}
Override
public void axisRangeChanged(AxisRangeEvent evt) {
if (evt.isChangedEvent() && evt.isVisibleRangeEvent())
update();
}
Override
public void axisChanged(AxisChangeEvent evt) {
}
Override
public void dataSourceChanged(DataSourceEvent evt) {
update();
}
private void update() {
IlvDataSet closeDS = getStockDataSource().getCloseDataSet();
if (closeDS != null) {
int firstIdx = (int) Math.ceil(mainChart.getXAxis().getVisibleMin());
if (firstIdx < closeDS.getDataCount()) {
double v = closeDS.getYData(firstIdx);
transform.setDefinition(100. / v, -100.);
setVisible(true);
return;
}
}
transform.setDefinition(1., 0.);
setVisible(false);
}
/**
* Shhows or hide the percentage variation.
*/
public void setVisible(boolean visible) {
if (this.visible == visible)
return;
this.visible = visible;
if (visible) {
mainChart.getChartArea().setMargins(null);
mainChart.setYScale(1, percentScale);
mainChart.setYGrid(1, percentGrid);
} else {
mainChart.getChartArea().setRightMargin(RIGHT_MARGIN);
mainChart.setYScale(1, null);
mainChart.setYGrid(1, null);
}
}
/**
* Activates or desactivates the display of percentage variation.
*/
public void setActive(boolean active) {
if (this.active == active)
return;
this.active = active;
if (active) {
update();
mainChart.getXAxis().addAxisListener(this);
getStockDataSource().addDataSourceListener(this);
setVisible(true);
} else {
setVisible(false);
mainChart.getXAxis().removeAxisListener(this);
getStockDataSource().removeDataSourceListener(this);
}
}
}
/** Creates the charts displaying lower indicators. */
protected IlvChart[] createLowerCharts() {
IlvChart[] charts = new IlvChart[LOWER_CHART_COUNT];
for (int i = 0; i < LOWER_CHART_COUNT; ++i) {
charts[i] = createLowerChart();
}
return charts;
}
/** Creates a lower chart. */
private IlvChart createLowerChart() {
IlvChart chart = createChart();
chart.synchronizeAxis(mainChart, IlvAxis.X_AXIS, true);
chart.setXScale(null);
chart.setXGrid(new SharedGrid(mainChart.getXScale(), GRID_COLOR));
IlvLegend legend = createLegend();
chart.setLegend(legend);
chart.setHeader(createHighlightComponent(chart, legend, 96));
return chart;
}
/** Creates the list of upper indicators. */
protected List<TechnicalIndicator> createUpperIndicators() {
List<TechnicalIndicator> list = new ArrayList<TechnicalIndicator>();
list.add(new BollingerBandsIndicator(stockDS));
list.add(new PriceChannelIndicator(stockDS));
list.add(MovingAverageIndicator.createSMA(stockDS, 20));
list.add(MovingAverageIndicator.createSMA(stockDS, 40));
list.add(MovingAverageIndicator.createEMA(stockDS, 20));
list.add(MovingAverageIndicator.createEMA(stockDS, 40));
return list;
}
/** Creates the list of lower indicators. */
protected List<TechnicalIndicator> createLowerIndicators() {
List<TechnicalIndicator> list = new ArrayList<TechnicalIndicator>();
list.add(new VolumeIndicator(stockDS));
list.add(new RSIIndicator(stockDS, 14));
list.add(MACDIndicator.createMACD(stockDS));
list.add(new WilliamsRIndicator(stockDS, 14));
list.add(StochasticIndicator.createFastStochastic(stockDS));
list.add(StochasticIndicator.createSlowStochastic(stockDS));
return list;
}
/** Initializes the actions associated with indicators. */
SuppressWarnings("unchecked")
private void initIndicatorActions() {
List<TechnicalIndicator> list = createUpperIndicators();
Iterator<TechnicalIndicator> ite = list.iterator();
upperIndicatorActions = new ArrayList<IndicatorAction>(list.size());
while (ite.hasNext()) {
TechnicalIndicator indicator = ite.next();
UpperIndicatorAction action = new UpperIndicatorAction(indicator);
action.updateEnabled();
upperIndicatorActions.add(action);
}
lowerIndicatorActions = new ArrayList[lowerCharts.length];
for (int i = 0; i < lowerIndicatorActions.length; ++i) {
lowerIndicatorActions[i] = new ArrayList<IndicatorAction>();
ite = createLowerIndicators().iterator();
while (ite.hasNext()) {
TechnicalIndicator indicator = ite.next();
LowerIndicatorAction action = new LowerIndicatorAction(indicator, i);
// action.updateEnabled();
lowerIndicatorActions[i].add(action);
}
}
}
// --------------------------------------------------------------------------
// - Menus & Toolbar
/** Creates the toolbar. */
Override
protected JToolBar createToolBar() {
JToolBar toolbar = new JToolBar();
toolbar.setFloatable(false);
populateToolBar(toolbar);
return toolbar;
}
/** Creates the menu bar. */
protected JMenuBar createMenuBar() {
JMenuBar menu = new JMenuBar();
menu.add(createDisplayMenu());
menu.add(createIndicatorsMenu());
return menu;
}
private JMenuItem getOverviewMenu() {
return getMenuBar().getMenu(0).getItem(1);
}
private JMenuItem getDetailedMenu() {
return getMenuBar().getMenu(0).getItem(0);
}
/** Creates the menu for display settings. */
protected JMenu createDisplayMenu() {
JMenu menu = new JMenu("Display");
JMenuItem item = null;
JMenu subMenu = new JMenu("Overview Representation");
menu.add(subMenu);
ButtonGroup group = new ButtonGroup();
item = new JRadioButtonMenuItem("Line", true);
item.setActionCommand(LINE_CMD);
group.add(item);
item.addActionListener(this);
subMenu.add(item);
item = new JRadioButtonMenuItem("Area");
item.setActionCommand(AREA_CMD);
group.add(item);
item.addActionListener(this);
subMenu.add(item);
item = new JRadioButtonMenuItem("Stair");
item.setActionCommand(STAIR_CMD);
group.add(item);
item.addActionListener(this);
subMenu.add(item);
item = new JRadioButtonMenuItem("Bar");
item.setActionCommand(BAR_CMD);
group.add(item);
item.addActionListener(this);
subMenu.add(item);
subMenu = new JMenu("Detailed Representation");
menu.add(subMenu);
group = new ButtonGroup();
item = new JRadioButtonMenuItem("Candle", true);
item.setActionCommand(CANDLE_CMD);
group.add(item);
item.addActionListener(this);
subMenu.add(item);
item = new JRadioButtonMenuItem("HLOC");
item.setActionCommand(HLOC_CMD);
group.add(item);
item.addActionListener(this);
subMenu.add(item);
menu.addSeparator();
item = new JCheckBoxMenuItem("Anti-aliasing", true);
item.addActionListener(new ActionListener() {
Override
public void actionPerformed(ActionEvent evt) {
setAntiAliasing(((JCheckBoxMenuItem) evt.getSource()).isSelected());
}
});
menu.add(item);
item = new JCheckBoxMenuItem("Logarithmic Price Axis");
item.addActionListener(new ActionListener() {
Override
public void actionPerformed(ActionEvent evt) {
boolean log = ((JCheckBoxMenuItem) evt.getSource()).isSelected();
if (log)
mainChart.getYAxis(0).setTransformer(new ExponentAxisTransformer(.5));
else
mainChart.getYAxis(0).setTransformer(null);
}
});
menu.add(item);
return menu;
}
/** Creates the menu that controls technical indicators. */
protected JMenu createIndicatorsMenu() {
// Initialize the actions for setting/removing the indicators.
initIndicatorActions();
JMenu menu = new JMenu("Indicators");
JMenu submenu = createUpperIndicatorMenu();
menu.add(submenu);
menu.addSeparator();
for (int i = 0; i < lowerCharts.length; ++i) {
submenu = createLowerIndicatorMenu(i);
menu.add(submenu);
}
return menu;
}
/** Creates the menu that controls upper indicators. */
protected JMenu createUpperIndicatorMenu() {
JMenu menu = new JMenu("Upper Indicators");
Iterator<IndicatorAction> ite = upperIndicatorActions.iterator();
while (ite.hasNext()) {
IndicatorAction action = ite.next();
JCheckBoxMenuItem item = new JCheckBoxMenuItem(action);
menu.add(item);
}
return menu;
}
/** Creates the menu that controls lower indicators. */
protected JMenu createLowerIndicatorMenu(int idx) {
String desc = "Lower Indicator" + (idx + 1);
JMenu menu = new JMenu(desc);
ButtonGroup group = new ButtonGroup();
int i = 0;
Iterator<IndicatorAction> ite = lowerIndicatorActions[idx].iterator();
while (ite.hasNext()) {
IndicatorAction action = ite.next();
JRadioButtonMenuItem item = new JRadioButtonMenuItem(action);
if (i == idx) {
setLowerIndicator(idx, action.getIndicator());
item.setSelected(true);
}
group.add(item);
menu.add(item);
++i;
}
return menu;
}
/**
* Populates the specified toolbar. This method is called by the
* <code>createToolBar()</code> method.
*/
Override
protected void populateToolBar(JToolBar toolbar) {
// == Refresh action.
IlvChartAction refreshAction = new IlvChartAction("Reload", null, null, "Reload Charts", null) {
Override
public void actionPerformed(ActionEvent evt) {
String symbol = queryPanel.getSymbol();
Date[] range = queryPanel.getQueryRange();
if (symbol == null || symbol.length() == 0) {
String msg = "Please enter a ticker symbol.";
JOptionPane.showMessageDialog(topFrame, msg, "", JOptionPane.WARNING_MESSAGE);
} else
loadQuotes(symbol, range[0], range[1], queryPanel.getFrequency());
}
};
refreshAction.setIcon(getClass(), "refresh.gif");
refreshAction.setChart(mainChart);
refreshButton = addAction(toolbar, refreshAction);
Dimension size = refreshButton.getPreferredSize();
toolbar.addSeparator();
// == Zoom History navigation.
backAction = new BackwardAction(zoomHistory);
backAction.setChart(mainChart);
addAction(toolbar, backAction);
fwdAction = new ForwardAction(zoomHistory);
fwdAction.setChart(mainChart);
addAction(toolbar, fwdAction);
fitAction = new IlvChartFitAction("Fit", null, null, "Reset Zoom", null) {
Override
public void actionPerformed(ActionEvent evt) {
StockUtil.performAnimatedZoom(mainChart, 0, mainChart.getXAxis().getDataRange(), StockUtil.ANIMATION_STEPS);
super.actionPerformed(evt);
zoomHistory.reset();
zoomHistory.add(mainChart.getXAxis().getVisibleRange());
}
};
fitAction.setIcon(getClass(), "fit.gif");
fitAction.setChart(mainChart);
addAction(toolbar, fitAction);
toolbar.addSeparator();
// == Local zoom
toolbar.add(createToggleButton("lzoom.gif", "lzoom_sel.gif", size, LOCALZOOM_CMD, "Local Zoom"));
zoomInAction = new LocalZoomAction(2.d, false);
zoomInAction.setChart(mainChart);
addAction(toolbar, zoomInAction);
zoomOutAction = new LocalZoomAction(2.f, true);
zoomOutAction.setChart(mainChart);
addAction(toolbar, zoomOutAction);
toolbar.addSeparator();
// == Price threshold
thresholdButton = createToggleButton("threshold.gif", "threshold_sel.gif", size, THRESHOLD_CMD, "Threshold Lines");
thresholdButton.setSelected(true);
toolbar.add(thresholdButton);
toolbar.setBorder(BorderFactory.createEtchedBorder());
}
// --------------------------------------------------------------------------
/**
* Handles <code>ActionEvent</code> events that occur on the registered
* listeners.
*/
Override
public void actionPerformed(ActionEvent evt) {
if (evt.getActionCommand().equals(StockQueryPanel.RELOAD_CMD)) {
String symbol = queryPanel.getSymbol();
if (symbol == null || symbol.length() == 0)
return;
Date[] range = queryPanel.getQueryRange();
loadQuotes(symbol, range[0], range[1], queryPanel.getFrequency());
} else if (evt.getActionCommand().equals(LINE_CMD)) {
setOverviewRepresentation(LINE);
} else if (evt.getActionCommand().equals(AREA_CMD)) {
setOverviewRepresentation(AREA);
} else if (evt.getActionCommand().equals(STAIR_CMD)) {
setOverviewRepresentation(STAIR);
} else if (evt.getActionCommand().equals(BAR_CMD)) {
setOverviewRepresentation(BAR);
} else if (evt.getActionCommand().equals(HLOC_CMD)) {
setDetailedRepresentation(HLOC);
} else if (evt.getActionCommand().equals(CANDLE_CMD)) {
setDetailedRepresentation(CANDLE);
} else if (evt.getActionCommand().equals(LOCALZOOM_CMD)) {
setLocalZoom(((JToggleButton) evt.getSource()).isSelected());
} else if (evt.getActionCommand().equals(THRESHOLD_CMD)) {
setThreshold(((JToggleButton) evt.getSource()).isSelected());
}
}
/** Toggles the local zoom. */
private void setLocalZoom(boolean set) {
if (set) {
double middle = mainChart.getXAxis().getVisibleRange().getMiddle();
double length = mainChart.getXAxis().getVisibleRange().getLength();
IlvLocalZoomAxisTransformer t = new IlvLocalZoomAxisTransformer(middle - length / 30, middle + length / 30, 3,
false);
mainChart.getXAxis().setTransformer(t);
LocalZoomHandler lzh = LocalZoomHandler.set(mainChart, lowerCharts, t);
lzh.setStartAnnotation(null);
lzh.setEndAnnotation(null);
mainChart.getXScale().setSkipLabelMode(IlvScale.ADAPTIVE_SKIP);
installLocalInteractors();
} else {
LocalZoomHandler.unset(mainChart, -1);
mainChart.getXScale().setSkipLabelMode(IlvScale.CONSTANT_SKIP);
installDefaultInteractors();
}
}
/** Toggles the price threshold. */
private synchronized void setThreshold(boolean set) {
int idx = toolBar.getComponentIndex(thresholdButton);
if (set) {
if (threshold == null) {
Color upperColor = new Color(120, 220, 120);
Color midColor = new Color(233, 150, 122);
Color lowerColor = IlvColor.indianRed;
threshold = new ThresholdLines(FOREGROUND, BACKGROUND, lowerColor, midColor, upperColor);
}
ThresholdLines.set(threshold, mainChart, 0);
toolBar.add(threshold.getRangeSlider(), idx + 1);
overviewRenderer.setRenderingHint(threshold);
detailedRenderer.setRenderingHint(threshold);
} else {
ThresholdLines threshold = ThresholdLines.remove(mainChart, 0);
if (threshold != null)
toolBar.remove(threshold.getRangeSlider());
overviewRenderer.setRenderingHint(null);
detailedRenderer.setRenderingHint(null);
}
toolBar.revalidate();
toolBar.repaint();
}
/** Installs the interactors when the local zoom is active. */
private void installLocalInteractors() {
fitAction.setChart(null);
backAction.setChart(null);
fwdAction.setChart(null);
zoomInAction.setChart(mainChart);
zoomOutAction.setChart(mainChart);
mainChart.setInteractors(upperInteractors[1]);
for (int i = 0; i < lowerCharts.length; ++i) {
lowerCharts[i].setInteractors(lowerInteractors[1][i]);
}
}
/** Installs the default interactors. */
private void installDefaultInteractors() {
fitAction.setChart(mainChart);
backAction.setChart(mainChart);
fwdAction.setChart(mainChart);
zoomInAction.setChart(null);
zoomOutAction.setChart(null);
mainChart.setInteractors(upperInteractors[0]);
for (int i = 0; i < lowerCharts.length; ++i) {
lowerCharts[i].setInteractors(lowerInteractors[0][i]);
}
}
/** Toggles anti-aliasing for the charts. */
public void setAntiAliasing(boolean b) {
if (antiAliasing == b)
return;
mainChart.setAntiAliasing(b);
mainChart.getChartArea().repaint();
for (int i = 0; i < lowerCharts.length; ++i) {
lowerCharts[i].setAntiAliasing(b);
lowerCharts[i].getChartArea().repaint();
}
antiAliasing = b;
}
/** Invoked before data is loaded. */
protected void startLoading() {
mainChart.getCoordinateSystem(0).getVisibleWindow();
if (loadingMessage == null) {
IlvLabelRenderer label = new IlvLabelRenderer();
label.setFont(new Font("Dialog", Font.BOLD, 18));
label.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(FOREGROUND),
BorderFactory.createEmptyBorder(8, 8, 8, 8)));
label.setOpaque(true);
label.setBackground(IlvColor.setAlpha(BACKGROUND, .8f));
loadingMessage = new ChartMessage("LOADING ...", label);
}
mainChart.addChartDrawListener(loadingMessage);
mainChart.getChartArea().repaint();
refreshButton.setEnabled(false);
}
/**
* Invoked when data has been loaded for both primary and secondary symbols.
*/
protected void endLoading() {
if (loadingMessage != null)
mainChart.removeChartDrawListener(loadingMessage);
mainChart.getChartArea().repaint();
refreshButton.setEnabled(true);
}
/** Loads the quotes. */
private void loadQuotes(final String symbol, Date startDate, Date endDate, int frequency) {
highlighter.unhighlight();
startLoading();
// == Reset all data
overviewRenderer.getDataSource().setDataSets(null);
detailedRenderer.getDataSource().setDataSets(null);
secondaryRenderer.getDataSource().setDataSets(null);
// == Workaround BUG IlvHiLoChartRenderer (setDataSets(null))
int mode = ((IlvHiLoChartRenderer) detailedRenderer).getMode();
if (mode == IlvHiLoChartRenderer.CANDLE)
setDetailedRepresentation(CANDLE);
else
setDetailedRepresentation(HLOC);
// == End Workaround
stockDS.reset();
updateIndicators();
StockDataSource.LoadHook afterLoad = new StockDataSource.LoadHook() {
Override
public void dataLoaded(StockDataSource stockDS) {
primaryDataLoaded(symbol);
}
Override
public void dataLoaded(IlvDoubleArray[] data) {
}
};
stockDS.loadData(symbol, startDate, endDate, frequency, afterLoad);
}
private void loadDefaultData() {
stockDS.loadDefaultData();
primaryDataLoaded(StockDataSource.DEFAULT_SYMBOL);
setThreshold(true);
threshold.setThresholds(new IlvDataInterval(25, 45));
}
private void primaryDataLoaded(String expectedSymbol) {
if (stockDS.getDataSetCount() > 0) {
String symbol = stockDS.getSymbol().toUpperCase();
if (!symbol.equalsIgnoreCase(expectedSymbol)) {
String msg = "Primary symbol [" + expectedSymbol + "] not found. Default data loaded";
JOptionPane.showMessageDialog(topFrame, msg, "Loading Error", JOptionPane.ERROR_MESSAGE);
}
// == Update overview Renderer
overviewRenderer.getDataSource().addDataSet(stockDS.getCloseDataSet());
overviewRenderer.setName(symbol);
// == Update detailed renderer
IlvDataSet[] quotes = { stockDS.getHighDataSet(), // high
stockDS.getLowDataSet(), // low
stockDS.getOpenDataSet(), // open
stockDS.getCloseDataSet() // close
};
detailedRenderer.getDataSource().setDataSets(quotes);
detailedRenderer.setName(symbol);
Color c = PRIMARY_COLOR;
IlvStyle style1 = IlvStyle.createStroked(c);
IlvStyle style2 = new IlvStyle(c, IlvColor.brighter(c));
IlvStyle style3 = new IlvStyle(c, IlvColor.darker(c));
detailedRenderer.setStyles(new IlvStyle[] { style1, style1, style2, style3 });
// == Load data for secondary symbols
final String[] secondarySymbols = queryPanel.getSecondarySymbols();
if (secondarySymbols != null) {
percentDisplayer.setActive(false);
StockDataSource.LoadHook afterLoad = new StockDataSource.LoadHook() {
Override
public void dataLoaded(StockDataSource stockDS) {
}
Override
public void dataLoaded(IlvDoubleArray[] data) {
secondaryDataLoaded(secondarySymbols, data);
}
};
stockDS.loadData(secondarySymbols, afterLoad);
} else {
percentDisplayer.setActive(true);
endLoading();
}
} else {
endLoading();
// Loading can be interrupted,
// String msg = "Cannot load data.";
// JOptionPane.showMessageDialog(topFrame, msg, "Loading Error",
// JOptionPane.ERROR_MESSAGE);
}
// checkSorted();
// == Update indicatore
updateIndicators();
// == Reset visible range and history.
resetVisibleRange();
zoomHistory.add(mainChart.getXAxis().getVisibleRange());
// == Update the x-scale
updateXScale();
// System.out.println("primaryDataLoaded");
}
private void secondaryDataLoaded(String[] symbols, IlvDoubleArray[] data) {
IlvDefaultDataSource dataSource = new IlvDefaultDataSource();
if (data != null) { // else loading was interrupted by another request
// Update the renderer for the secondary symbols.
String error = null;
for (int i = 0; i < data.length; ++i) {
if (data[i] == null) {
if (error == null)
error = symbols[i];
else
error += ", " + symbols[i];
continue;
}
String name = symbols[i].toUpperCase();
data[i].trim();
dataSource.addDataSet(new IlvDefaultDataSet(name, data[i].data()));
}
if (error != null) {
String msg = "The following secondary symbol(s) were not found: " + error;
JOptionPane.showMessageDialog(topFrame, msg, "Loading Error", JOptionPane.ERROR_MESSAGE);
}
}
secondaryRenderer.setDataSource(dataSource);
endLoading();
}
/**
* Invoked when quotes are loaded for the primary symbol. This method updates
* the steps definition of the x-scale.
*/
private void updateXScale() {
Date[] dates = stockDS.getDates();
if (dates == null) {
mainChart.getXScale().setVisible(false);
mainChart.getXScale().setStepsDefinition(new IlvDefaultStepsDefinition());
} else {
IlvTimeUnit unit = null;
switch (stockDS.getFrequency()) {
case StockDataSource.MONTHLY:
unit = IlvTimeUnit.MONTH;
break;
case StockDataSource.WEEKLY:
unit = IlvTimeUnit.WEEK;
break;
case StockDataSource.DAILY:
default:
unit = IlvTimeUnit.DAY;
break;
}
CategoryTimeSteps def;
def = new CategoryTimeSteps(dates, unit);
mainChart.getXScale().setStepsDefinition(def);
mainChart.getXScale().setVisible(true);
}
}
/**
* Invoked when quotes are loaded for the primary symbol. This method updates
* the indicators and the enabled state of their associated actions
*/
private void updateIndicators() {
// Refresh indicators.
for (int i = 0; i < lowerIndicators.length; ++i) {
if (lowerIndicators[i] != null)
lowerIndicators[i].refresh();
}
Iterator<TechnicalIndicator> ite = upperIndicators.iterator();
while (ite.hasNext())
ite.next().refresh();
// Update the enabled state of actions.
Iterator<IndicatorAction> ite2 = upperIndicatorActions.iterator();
while (ite2.hasNext()) {
ite2.next().updateEnabled();
}
}
private void resetVisibleRange() {
mainChart.getXAxis().setAutoVisibleRange(true);
mainChart.getYAxis(0).setAutoVisibleRange(true);
zoomHistory.reset();
}
/**
* Creates a legend for the charts.
*/
protected IlvLegend createLegend() {
IlvLegend legend = new IlvLegend();
legend.setLayout(new FlowLayout(FlowLayout.LEFT, 12, 2));
legend.setPaintingBackground(false);
legend.setBorder(null);
legend.setForeground(FOREGROUND);
legend.setMovable(false);
legend.setFont(DEFAULT_FONT);
legend.setSymbolSize(new Dimension(16, 12));
return legend;
}
/**
* Creates a new <code>IlvChart</code> and sets common properties.
*/
protected IlvChart createChart() {
IlvChart chart = new IlvChart();
chart.getXGrid().setMajorPaint(GRID_COLOR);
chart.getYGrid(0).setMajorPaint(GRID_COLOR);
chart.getXGrid().setDrawOrder(-2);
chart.getYGrid(0).setDrawOrder(-2);
chart.setForeground(FOREGROUND);
chart.setFont(DEFAULT_FONT);
IlvStyle style = chart.getChartArea().getPlotStyle();
if (style != null)
chart.getChartArea().setPlotStyle(style.setFillOn(false));
return chart;
}
/**
* Creates the chart interactors.
*/
private void createInteractors() {
// == Upper interactors
upperInteractors[0] = new IlvChartInteractor[5];
// Create the interactor that highlights quotes.
HighlightQuotesInteractor highlightInter = new HighlightQuotesInteractor() {
Override
protected boolean isTarget(IlvChartRenderer r) {
return r == overviewRenderer || r == detailedRenderer || r.getParent() == detailedRenderer;
}
};
// Add a listener to handle highlighting.
highlighter = new Highlighter();
highlightInter.addChartInteractionListener(highlighter);
upperInteractors[0][0] = highlightInter;
// Add zooming behavior. Subclassed to store the new visible range
// in the axis zoom history.
AutoYZoomInteractor autoYZoomInter = new AutoYZoomInteractor() {
Override
protected void doIt() {
IlvDataInterval oldRange = getXAxis().getVisibleRange();
super.doIt();
IlvDataInterval newRange = getXAxis().getVisibleRange();
if (!oldRange.equals(newRange))
zoomHistory.add(newRange);
}
};
autoYZoomInter.setAnimationStep(StockUtil.ANIMATION_STEPS);
upperInteractors[0][1] = autoYZoomInter;
// Add panning behavior
upperInteractors[0][2] = new IlvChartPanInteractor();
upperInteractors[0][3] = new IlvChartXScrollInteractor();
// Add zooming behavior on the scale
ZoomScaleInteractor zoomScaleInter = new ZoomScaleInteractor(-1) {
Override
protected void zoomScale() {
super.zoomScale();
IlvDataInterval itv = getXAxis().getVisibleRange();
zoomHistory.add(itv);
}
};
upperInteractors[0][4] = zoomScaleInter;
// <code>localInters</code> stores interactors related to local zoom
// interactions.
upperInteractors[1] = new IlvChartInteractor[3];
upperInteractors[1][0] = new IlvChartLocalPanInteractor(0, MouseEvent.BUTTON3_DOWN_MASK);
upperInteractors[1][1] = new IlvChartLocalReshapeInteractor() {
// == Workaround limitation (adjusting state not set on the axis).
Override
protected void startOperation(MouseEvent evt) {
super.startOperation(evt);
mainChart.getXAxis().setAdjusting(true);
}
Override
protected void endOperation(MouseEvent evt) {
super.endOperation(evt);
mainChart.getXAxis().setAdjusting(false);
}
};
upperInteractors[1][2] = highlightInter;
// == Lower interactors
lowerInteractors[0] = new IlvChartInteractor[lowerCharts.length][];
lowerInteractors[1] = new IlvChartInteractor[lowerCharts.length][];
for (int i = 0; i < lowerCharts.length; ++i) {
lowerInteractors[0][i] = new IlvChartInteractor[] { new IlvChartPanInteractor() };
lowerInteractors[1][i] = null;
}
}
/**
* Invoked when the visible time range changes.
* <p>
* This method toggles the visibility of the quote overview/quote renderers
* according to the visible time range.
*/
private void xRangeChanged(boolean zoom) {
IlvDataInterval xRange = mainChart.getXAxis().getVisibleRange();
if (xRange.isEmpty())
return;
IlvDataInterval yRange = StockUtil.getYDataRange(mainChart, 0, xRange);
if (!yRange.isEmpty())
mainChart.getYAxis(0).setVisibleRange(yRange);
if (zoom) {
boolean hideOverview = (xRange.getLength() < OVERVIEW_THRESHOLD);
mainChart.getYAxis(0).setAutoDataRange(false);
getDetailedMenu().setEnabled(!hideOverview);
getOverviewMenu().setEnabled(hideOverview);
overviewRenderer.setVisible(!hideOverview);
detailedRenderer.setVisible(hideOverview);
mainChart.getYAxis(0).setAutoDataRange(true);
LocalZoomHandler lzh = LocalZoomHandler.get(mainChart, -1);
if (lzh != null) {
double middle = xRange.getMiddle();
double length = xRange.getLength();
lzh.getTransformer().setZoomRange(middle - length / 30, middle + length / 30);
}
}
}
/**
* Specifies the stock overview representation.
*
* @param representation
* The desired representation.
* @see #BAR
* @see #LINE
* @see #AREA
* @see #STAIR
*/
public void setOverviewRepresentation(int representation) {
int idx = mainChart.getRenderers().indexOf(overviewRenderer);
IlvChartRenderer r = null;
Color c = DEFAULT_COLORS[0];
// Stroke stroke = new BasicStroke(2.f, BasicStroke.CAP_ROUND,
// BasicStroke.JOIN_ROUND);
IlvStyle filledStyle = new IlvStyle(c, IlvColor.setAlpha(c, .45f));
IlvStyle strokedStyle = new IlvStyle(2.f, c);
switch (representation) {
case BAR:
r = new IlvSingleBarRenderer(filledStyle, 100.);
break;
case STAIR:
r = new IlvSingleStairRenderer(filledStyle);
break;
case AREA:
r = new IlvSingleAreaRenderer(filledStyle);
break;
case LINE:
default:
r = new IlvSinglePolylineRenderer(strokedStyle);
break;
}
if (idx == -1) {
mainChart.addRenderer(r);
} else {
r.setVisible(overviewRenderer.isVisible());
r.setName(overviewRenderer.getName());
mainChart.setRenderer(idx, r);
// Update y-range explicitly since the setRenderer can modify
// the x-range before the new renderer is added.
xRangeChanged(false);
}
overviewRenderer = r;
}
/**
* Specifies the stock overview representation.
*
* @param representation
* The desired representation.
* @see #HLOC
* @see #CANDLE
*/
public void setDetailedRepresentation(int representation) {
int idx = mainChart.getRenderers().indexOf(detailedRenderer);
IlvChartRenderer r = null;
switch (representation) {
case HLOC:
r = new IlvHiLoChartRenderer(IlvHiLoChartRenderer.OPENCLOSE, 0, 80);
break;
case CANDLE:
default:
r = new IlvHiLoChartRenderer(IlvHiLoChartRenderer.CANDLE, 0, 80);
break;
}
if (idx == -1)
mainChart.addRenderer(r);
else {
r.setVisible(detailedRenderer.isVisible());
r.setName(detailedRenderer.getName());
IlvStyle[] styles = detailedRenderer.getStyles();
mainChart.setRenderer(idx, r);
r.setStyles(styles);
xRangeChanged(false);
}
detailedRenderer = r;
}
/**
* Returns the default start date for quote queries.
*
* @return One year before the current date.
*/
static Date getDefaultStartDate() {
Calendar cal = IlvCalendarFactory.createInstance(IlvLocaleUtil.getCurrentULocale());
cal.setTime(new Date());
cal.add(Calendar.YEAR, -1);
return cal.getTime();
}
/** Creates a label with a default font and foreground */
private static JLabel createLabel(String text) {
JLabel label = new JLabel(text);
label.setForeground(FOREGROUND);
label.setFont(DEFAULT_FONT);
return label;
}
/** Returns the highlight label for the specified chart. */
private static JLabel getHighlightStatus(IlvChart chart) {
return (JLabel) chart.getClientProperty(HIGHLIGHT_STATUS_KEY);
}
/** Creates a toggle button for the toolbar */
private JToggleButton createToggleButton(String iconName, String selIconName, Dimension size, String actionCommand,
String actionDesc) {
Icon icon = StockUtil.loadIcon(iconName);
JToggleButton toggle = new JToggleButton(icon);
icon = StockUtil.loadIcon(selIconName);
toggle.setSelectedIcon(icon);
toggle.setPreferredSize(size);
toggle.setMaximumSize(size);
toggle.setMinimumSize(size);
toggle.setActionCommand(actionCommand);
toggle.setToolTipText(actionDesc);
toggle.addActionListener(this);
return toggle;
}
/**
* Adds the specified action to a toolbar and returns the created
* <code>JButton</code>.
*/
Override
protected JButton addAction(JToolBar toolbar, IlvChartAction action) {
JButton button = super.addAction(toolbar, action);
button.setPreferredSize(new Dimension(28, 28));
return button;
}
/**
* Application mainline.
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
Override
public void run() {
JFrame topFrame = new JFrame("Rogue Wave JViews Charts Stock Demo");
StockDemo demo = new StockDemo();
demo.init(topFrame.getContentPane());
demo.setFrameGeometry(600, 700, true);
topFrame.setVisible(true);
}
});
}
// --------------------------------------------------------------------------
/**
* The base action class for setting indicators.
*/
abstract class IndicatorAction extends AbstractAction {
private TechnicalIndicator indicator;
/**
* Creates a new <code>UpperIndicatorAction</code> that references the
* specified indicator.
*/
public IndicatorAction(TechnicalIndicator indicator) {
super(indicator.getName());
this.indicator = indicator;
}
/**
* Computes the enable state of the action.
*/
public void updateEnabled() {
boolean enabled = (stockDS.getDataSetCount() > 0);
setEnabled(enabled);
}
/**
* Returns the indicator associated with this action.
*/
public final TechnicalIndicator getIndicator() {
return indicator;
}
}
/**
* An action class used to add upper indicators.
*/
class UpperIndicatorAction extends IndicatorAction {
private boolean added;
/**
* Creates a new <code>UpperIndicatorAction</code> that references the
* specified indicator.
*/
public UpperIndicatorAction(TechnicalIndicator indicator) {
super(indicator);
}
/** Invoked when an action occurs. */
Override
public void actionPerformed(ActionEvent evt) {
if (!isEnabled())
return;
if (added) {
removeUpperIndicator(getIndicator());
added = false;
} else {
addUpperIndicator(getIndicator());
added = true;
}
}
}
/**
* An action class used to set lower indicators.
*/
class LowerIndicatorAction extends IndicatorAction {
private int chartIdx;
/**
* Creates a new <code>LowerIndicatorAction</code> that references the
* specified indicator.
*/
public LowerIndicatorAction(TechnicalIndicator indicator, int chartIdx) {
super(indicator);
this.chartIdx = chartIdx;
}
/** Invoked when an action occurs. */
Override
public void actionPerformed(ActionEvent evt) {
if (!isEnabled())
return;
setLowerIndicator(chartIdx, getIndicator());
}
}
// --------------------------------------------------------------------------
/**
* Listens to the events sent by the highlight interactor and performs the
* highlighting operations.
*/
private class Highlighter implements ChartInteractionListener {
private HighlightIndicator upperHighlight;
private HighlightIndicator[] lowerHighlights;
public Highlighter() {
upperHighlight = createHighlightIndicator();
mainChart.addDecoration(upperHighlight);
int count = lowerCharts.length;
lowerHighlights = new HighlightIndicator[count];
for (int i = 0; i < count; ++i) {
lowerHighlights[i] = createHighlightIndicator();
lowerCharts[i].addDecoration(lowerHighlights[i]);
}
}
Override
public void interactionPerformed(ChartInteractionEvent evt) {
ChartHighlightInteractionEvent hevt = (ChartHighlightInteractionEvent) evt;
if (hevt.isHighlighted())
highlight(hevt.getDisplayPoint());
else
unhighlight();
}
private void highlight(IlvDisplayPoint hdp) {
if (detailedRenderer.isVisible()) {
hdp = detailedRenderer.getDisplayPoint(stockDS.getCloseDataSet(), hdp.getIndex());
if (hdp == null)
return;
}
upperHighlight.setHighlightedPoint(hdp);
for (int i = 0; i < lowerHighlights.length; ++i) {
IlvDisplayPoint idp = lowerIndicators[i].getHighlightedPoint(hdp.getIndex());
lowerHighlights[i].setHighlightedPoint(idp);
if (idp == null) {
lowerHighlights[i].setValue(hdp.getXData());
if (!lowerHighlights[i].isVisible())
lowerHighlights[i].setVisible(true);
} else {
lowerHighlights[i].setVisible(true);
JLabel status = getHighlightStatus(lowerCharts[i]);
if (status != null && idp != null) {
status.setText(numFmt.format(idp.getYData()));
}
}
}
quoteDisplay.displayQuote(hdp);
}
private void unhighlight() {
upperHighlight.setHighlightedPoint(null);
for (int i = 0; i < lowerHighlights.length; ++i) {
lowerHighlights[i].setHighlightedPoint(null);
if (lowerHighlights[i].isVisible())
lowerHighlights[i].setVisible(false);
JLabel status = getHighlightStatus(lowerCharts[i]);
if (status != null)
status.setText("");
}
quoteDisplay.displayQuote(null);
}
private HighlightIndicator createHighlightIndicator() {
HighlightIndicator indic = new HighlightIndicator(-1);
indic.setDrawOrder(2);
indic.setColor(FOREGROUND);
indic.setVisible(false);
return indic;
}
}
/** A panel displaying highlighed quote values. */
private class QuoteDisplayPanel extends JPanel {
private final int WIDTH = 62;
private final int SPACE = 6;
JLabel date, high, low, open, close;
public QuoteDisplayPanel() {
super(new BorderLayout());
setBorder(BorderFactory.createEmptyBorder(2, 12, 2, 4));
Box box = Box.createHorizontalBox();
setOpaque(false);
box.add(createLabel("DATE"));
box.add(Box.createHorizontalStrut(SPACE));
date = new FixedLabel(WIDTH, JLabel.RIGHT);
box.add(date);
box.add(Box.createHorizontalGlue());
box.add(createLabel("OPEN"));
box.add(Box.createHorizontalStrut(SPACE));
open = new FixedLabel(WIDTH, JLabel.RIGHT);
box.add(open);
box.add(Box.createHorizontalGlue());
box.add(createLabel("HIGH"));
box.add(Box.createHorizontalStrut(SPACE));
high = new FixedLabel(WIDTH, JLabel.RIGHT);
box.add(high);
box.add(Box.createHorizontalGlue());
box.add(createLabel("LOW"));
box.add(Box.createHorizontalStrut(SPACE));
low = new FixedLabel(WIDTH, JLabel.RIGHT);
box.add(low);
box.add(Box.createHorizontalGlue());
box.add(createLabel("CLOSE"));
box.add(Box.createHorizontalStrut(SPACE));
close = new FixedLabel(WIDTH, JLabel.RIGHT);
box.add(close);
box.add(Box.createHorizontalGlue());
add(box);
}
public void displayQuote(IlvDisplayPoint dp) {
if (stockDS.getDataSetCount() < 4 || dp == null) {
date.setText(" ");
open.setText(" ");
high.setText(" ");
low.setText(" ");
close.setText(" ");
} else {
int idx = dp.getIndex();
date.setText(dateFmt.format(stockDS.getDate(idx)));
double val = stockDS.getOpenDataSet().getYData(idx);
open.setText(numFmt.format(val));
val = stockDS.getHighDataSet().getYData(idx);
high.setText(numFmt.format(val));
val = stockDS.getLowDataSet().getYData(idx);
low.setText(numFmt.format(val));
val = stockDS.getCloseDataSet().getYData(idx);
close.setText(numFmt.format(val));
}
}
}
}