package models;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import java.util.ArrayList;
import javax.swing.BoxLayout;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
/**
* A class that implements the ActionListener interface specifically
* to attribute an action to the app's "Lookup" button
* @author Oladimeji Ogunyoye
*
*/
public class lookupButtonAction implements ActionListener
{
private JTextField ins;
private String[][] dataArray;
private JComboBox dayStart;
private JComboBox monthStart;
private JComboBox yearStart;
private JComboBox dayEnd;
private JComboBox monthEnd;
private JComboBox yearEnd;
private JComboBox interval;
private JCheckBox order;
private Double[] AdjClose;
private Double[] AdjCloseReversed;
private ArrayList<JFrame> frames;
private DecimalFormat twoDP;
/**
* Constructor which requires the user modified widgets. The state of
* these widgets determine what data is returned.
*
* @param ins - input field (Stock Symbol)
* @param dayStart - the starting day from a combo box
* @param monthStart - the starting month from a combo box
* @param yearStart - the starting year from a combo box
* @param dayEnd - the ending day from a combo box
* @param monthEnd - the ending month from a combo box
* @param yearEnd - the ending year from a combo box
* @param interval - the periods at which data is returned (Monthly, Weekly, Daily) from a combo box
*/
public lookupButtonAction(JTextField ins, JComboBox dayStart, JComboBox monthStart, JComboBox yearStart, JComboBox dayEnd,
JComboBox monthEnd, JComboBox yearEnd, JComboBox interval, JCheckBox order)
{
this.ins = ins;
this.dayStart = dayStart;
this.monthStart = monthStart;
this.yearStart = yearStart;
this.dayEnd = dayEnd;
this.monthEnd = monthEnd;
this.yearEnd = yearEnd;
this.interval = interval;
this.order = order;
frames = new ArrayList<JFrame>();
twoDP = new DecimalFormat("0.00"); // used to round average and maximal drawdown to 2 decimal places
}
/**
* Inherited abstract method that invokes an action for the "Lookup"
* button. The method retrieves data from the input field, combo boxes and
* checkbox. It uses this data to form the URL string and subsequently retrieve
* data from the Yahoo iChart service.
*/
@Override
public void actionPerformed(ActionEvent arg0)
{
String x = "";
JFrame error = new JFrame();
final JFrame stockDataFrame = new JFrame();
if(!(ins.getText().matches("[A-Z]{1,8}(.*)"))) // catches any invalid stock entries
{
JOptionPane.showMessageDialog(error,"ERROR!\n" + "\"" + ins.getText() + "\"" + "must be maximum 8 characters in length(uppercase, digits and period)!");
}
else if((yearStart.getSelectedIndex() > yearEnd.getSelectedIndex() && monthStart.getSelectedIndex() > monthEnd.getSelectedIndex() && dayStart.getSelectedIndex() > dayEnd.getSelectedIndex())
||(yearStart.getSelectedIndex() > yearEnd.getSelectedIndex() && monthStart.getSelectedIndex() > monthEnd.getSelectedIndex() && dayEnd.getSelectedIndex() > dayStart.getSelectedIndex())
||(monthStart.getSelectedIndex() > monthEnd.getSelectedIndex() && dayStart.getSelectedIndex() > dayEnd.getSelectedIndex() && yearEnd.getSelectedIndex() > yearStart.getSelectedIndex())
||(dayStart.getSelectedIndex() > dayEnd.getSelectedIndex() && yearStart.getSelectedIndex() > yearEnd.getSelectedIndex() && monthEnd.getSelectedIndex() > monthStart.getSelectedIndex())
||(dayStart.getSelectedIndex() > dayEnd.getSelectedIndex()) || (yearStart.getSelectedIndex() > yearEnd.getSelectedIndex()) || (monthStart.getSelectedIndex() > monthEnd.getSelectedIndex())
||(monthStart.getSelectedIndex() == monthEnd.getSelectedIndex() && yearStart.getSelectedIndex() == yearEnd.getSelectedIndex() && dayStart.getSelectedIndex() == dayEnd.getSelectedIndex()))
{
JOptionPane.showMessageDialog(error, "ERROR!\n" + "Your start date must be BEFORE your end date!");
}
else
{
x = ins.getText();
dayStart.getSelectedIndex();
String x1 = (String) yearStart.getSelectedItem();
String x2 = (String) yearEnd.getSelectedItem();
String data = URLReader.readURL("http://ichart.yahoo.com/table.csv?s=" + x + "&a=" + monthStart.getSelectedIndex() + "&b=" + (dayStart.getSelectedIndex()+1)
+ "&c=" + x1 + "&d=" + monthEnd.getSelectedIndex() + "&e=" + (dayEnd.getSelectedIndex()+1) + "&f=" + x2 + "&g=" + getInterval(interval));
System.out.print("http://ichart.yahoo.com/table.csv?s=" + x + "&a=" + monthStart.getSelectedIndex() + "&b=" + (dayStart.getSelectedIndex()+1)
+ "&c=" + x1 + "&d=" + monthEnd.getSelectedIndex() + "&e=" + (dayEnd.getSelectedIndex()+1) + "&f=" + x2 + "&g=" + getInterval(interval));
String FrameTitle = ins.getText() + ": " + x1 + "/" + (String)monthStart.getSelectedItem() + "/" + (dayStart.getSelectedIndex()+1) + " to "
+ x2 + "/" + (String)monthEnd.getSelectedItem() + "/" + (dayEnd.getSelectedIndex()+1) + " (" + (String)interval.getSelectedItem() + ")";
int numOfRows = data.split("\n").length;
int numOfCols = 7;
String[] temp = data.replaceAll("\n", ",").split(",");
String[] names = {"Date", "Open", "High", "Low", "Close", "Volume", "Adj Close"};
dataArray = new String[numOfRows-1][numOfCols];
for(int i = 0; i < numOfRows-1; i++)
{
for(int j = 0; j < 7; j++)
{
if(numOfCols < temp.length)
{
dataArray[i][j] = temp[numOfCols];
numOfCols++;
}
}
}
AdjClose = new Double[numOfRows-1];
JTable jtSpread = new JTable(dataArray,names);
Double total = 0.0;
for(int i = 0; i < numOfRows-1; i++)
{
AdjClose[i] = Double.parseDouble(dataArray[i][6]);
total = (AdjClose[i] + total);
}
Double average = total/AdjClose.length;
String roundedaverage = twoDP.format(average);
if(order.isSelected()) // loop that initiates if the chronological order checkbox has been selected
{
int xs = temp.length-1;
AdjCloseReversed = new Double[numOfRows-1];
for(int i = 0; i < numOfRows-1; i++)
{
for(int j = 0; j < 7; j++)
{
if(xs > 6)
{
dataArray[i][j] = temp[xs];
xs--;
}
}
}
for(int i = 0; i < numOfRows-1; i++)
{
AdjCloseReversed[i] = Double.parseDouble(dataArray[i][0]);
}
for(int i = numOfRows-2; i > 0; i--)
{
if(AdjCloseReversed[i] > AdjCloseReversed[i-1])
{
jtSpread.setValueAt("<html><font color=\"green\">" + dataArray[i][0] + "</font></html>", i, 0);
}
else if(AdjCloseReversed[i] < AdjCloseReversed[i-1])
{
jtSpread.setValueAt("<html><font color=\"red\">" + dataArray[i][0] + "</font></html>", i, 0);
}
}
for(int i = 0; i < 6; i++) // switches the order of the columns to the default order
{
jtSpread.moveColumn(6, i);
}
for(int i = 0; i < names.length; i++)
{
jtSpread.getColumnModel().getColumn(i).setHeaderValue(names[i]);
}
FrameTitle = FrameTitle + " Chronological";
}
else
{
for(int i = 0; i < numOfRows-2; i++)
{
if(AdjClose[i] > AdjClose[i+1])
{
jtSpread.setValueAt("<html><font color=\"green\">" + dataArray[i][6] + "</font></html>", i, 6);
}
else if(AdjClose[i] < AdjClose[i+1])
{
jtSpread.setValueAt("<html><font color=\"red\">" + dataArray[i][6] + "</font></html>", i, 6);
}
}
FrameTitle = FrameTitle + " Reverse Chronological";
}
JScrollPane jsp = new JScrollPane(jtSpread);
JPanel statusPane = new JPanel();
JLabel statusInfo = new JLabel("Average: " + roundedaverage);
JLabel statusInfo2 = new JLabel("Maximal Drawdown: " + calcMDD(AdjClose));
statusPane.setLayout(new BoxLayout(statusPane,BoxLayout.Y_AXIS));
statusPane.add(statusInfo);
statusPane.add(statusInfo2);
stockDataFrame.setTitle(FrameTitle);
stockDataFrame.add(jsp, BorderLayout.CENTER);
stockDataFrame.add(statusPane, BorderLayout.SOUTH);
stockDataFrame.pack();
stockDataFrame.setVisible(true);
for(int i = 0; i < frames.size(); i++)
{
if(frames.get(i).getTitle().equals(stockDataFrame.getTitle()))
{
stockDataFrame.toBack();
stockDataFrame.setVisible(false);
stockDataFrame.dispose();
frames.get(i).toFront();
frames.get(i).setVisible(true);
break;
}
}
frames.add(stockDataFrame);
ins.setText("");
}
}
/**
* A method that returns the selected interval into a
* character, enabling it to be used in the URL query.
*
* @param interval
* @return
*/
private String getInterval(JComboBox interval)
{
String intervalString = "";
if(interval.getSelectedIndex() == 0)
{
intervalString = "m";
}
else if(interval.getSelectedIndex() == 1)
{
intervalString = "d";
}
else
{
intervalString = "w";
}
return intervalString;
}
/**
* A method that calculates the maximal drawdown of a given stock.
*
* @param x - the array that is iterated through to calculate the maximal drawdown
* @return the maximal drawdown of the stock
*/
private String calcMDD(Double[] adjCloseArray) // returns the maximal drawdown of the Stock
{
double MDD = 0;
double peak = -99999;
for(int i = adjCloseArray.length-1; i >= 0; i--)
{
double current = adjCloseArray[i];
if (current > peak)
{
peak = current;
}
else
{
double drawDown = peak - current;
if (drawDown > MDD)
{
MDD = drawDown;
}
}
}
return twoDP.format(MDD); // returns a string which is the MDD rounded to 2 decimal places
}
}