/* ===========================================================
* TradeManager : a application to trade strategies for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2011-2011, by Simon Allen and Contributors.
*
* Project Info: org.trade
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Oracle, Inc.
* in the United States and other countries.]
*
* (C) Copyright 2011-2011, by Simon Allen and Contributors.
*
* Original Author: Simon Allen;
* Contributor(s): -;
*
* Changes
* -------
*
*/
package org.trade.ui.portfolio;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.time.ZonedDateTime;
import java.util.Vector;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JSplitPane;
import javax.swing.JToolBar;
import javax.swing.ListSelectionModel;
import javax.swing.RowSorter;
import javax.swing.SpinnerDateModel;
import javax.swing.border.BevelBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.MaskFormatter;
import org.trade.core.properties.ConfigProperties;
import org.trade.core.util.TradingCalendar;
import org.trade.core.valuetype.Date;
import org.trade.core.valuetype.Decode;
import org.trade.core.valuetype.Money;
import org.trade.core.valuetype.ValueTypeException;
import org.trade.dictionary.valuetype.DAOPortfolio;
import org.trade.persistent.PersistentModel;
import org.trade.persistent.dao.Portfolio;
import org.trade.persistent.dao.TradelogDetail;
import org.trade.persistent.dao.TradelogReport;
import org.trade.persistent.dao.TradelogSummary;
import org.trade.ui.base.BaseButton;
import org.trade.ui.base.BasePanel;
import org.trade.ui.base.BaseUIPropertyCodes;
import org.trade.ui.base.ExampleFileChooser;
import org.trade.ui.base.ExampleFileFilter;
import org.trade.ui.base.FilePreviewer;
import org.trade.ui.base.Table;
import org.trade.ui.models.TradelogDetailTableModel;
import org.trade.ui.models.TradelogSummaryTableModel;
import org.trade.ui.tables.TradelogDetailTable;
import org.trade.ui.tables.TradelogSummaryTable;
import org.trade.ui.widget.DAODecodeComboBoxEditor;
import org.trade.ui.widget.DecodeComboBoxRenderer;
import org.trade.ui.widget.MoneyField;
import org.trade.ui.widget.StringField;
/**
*/
public class PortfolioPanel extends BasePanel implements ChangeListener, ItemListener {
private static final long serialVersionUID = 98016024273398947L;
private PersistentModel m_tradePersistentModel = null;
private TradelogReport m_tradelogReport = new TradelogReport();
private String m_csvDefaultDir = null;
private Table m_tableTradelogSummary = null;
private TradelogSummaryTableModel m_tradelogSummaryModel = null;
private Table m_tableTradelogDetail = null;
private TradelogDetailTableModel m_tradelogDetailModel = null;
private BaseButton transferButton = null;
private JSpinner spinnerStart = new JSpinner();
private JSpinner spinnerEnd = new JSpinner();
private JCheckBox filterButton = new JCheckBox("Show not traded");
private StringField symbolField = null;
private DAODecodeComboBoxEditor portfolioEditorComboBox = null;
private static final String DATEFORMAT = "MM/dd/yyyy";
private TradelogDetail selectedTradelogDetail = null;
private Portfolio portfolio = null;
private MoneyField m_lossGainAmt = new MoneyField();
private static final String MASK = "**********";
private static final String VALID_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456789. ";
private static final String PLACE_HOLDER = " ";
/**
* Constructor for PortfolioPanel.
*
* @param controller
* BasePanel
* @param tradePersistentModel
* PersistentModel
*/
public PortfolioPanel(BasePanel controller, PersistentModel tradePersistentModel) {
try {
if (null != getMenu())
getMenu().addMessageListener(this);
this.setLayout(new BorderLayout());
m_tradePersistentModel = tradePersistentModel;
m_csvDefaultDir = ConfigProperties.getPropAsString("trade.csv.default.dir");
transferButton = new BaseButton(controller, BaseUIPropertyCodes.TRANSFER);
JLabel portfolioLabel = new JLabel("Portfolio:");
portfolioEditorComboBox = new DAODecodeComboBoxEditor(DAOPortfolio.newInstance().getCodesDecodes());
DecodeComboBoxRenderer portfolioRenderer = new DecodeComboBoxRenderer();
portfolioEditorComboBox.setRenderer(portfolioRenderer);
this.portfolio = (Portfolio) DAOPortfolio.newInstance().getObject();
portfolioEditorComboBox.setItem(DAOPortfolio.newInstance());
portfolioEditorComboBox.addItemListener(this);
m_tradelogSummaryModel = new TradelogSummaryTableModel();
m_tableTradelogSummary = new TradelogSummaryTable(m_tradelogSummaryModel);
m_tradelogDetailModel = new TradelogDetailTableModel();
m_tableTradelogDetail = new TradelogDetailTable(m_tradelogDetailModel);
spinnerStart.setModel(new SpinnerDateModel());
JSpinner.DateEditor dateStart = new JSpinner.DateEditor(spinnerStart, DATEFORMAT);
spinnerStart.setEditor(dateStart);
spinnerStart.setValue((new Date(TradingCalendar.getYearStart())).getDate());
spinnerEnd.setModel(new SpinnerDateModel());
JSpinner.DateEditor dateEnd = new JSpinner.DateEditor(spinnerEnd, DATEFORMAT);
spinnerEnd.setEditor(dateEnd);
spinnerEnd.setValue(
(new Date(TradingCalendar.getTradingDayStart(TradingCalendar.getDateTimeNowMarketTimeZone())))
.getDate());
filterButton.setSelected(false);
JPanel jPanel1 = new JPanel(new FlowLayout(FlowLayout.RIGHT));
jPanel1.setBorder(new BevelBorder(BevelBorder.RAISED));
JLabel jLabelSummary = new JLabel("Ignor in Batting/Sharpe $ under");
m_lossGainAmt.setBorder(new BevelBorder(BevelBorder.LOWERED));
jPanel1.add(jLabelSummary, null);
jPanel1.add(m_lossGainAmt, null);
JPanel jPanel2 = new JPanel(new FlowLayout(FlowLayout.LEFT));
JLabel startLabel = new JLabel("Start Date:");
JLabel endLabel = new JLabel("End Date:");
JLabel symbolLabel = new JLabel("Symbol:");
symbolField = new StringField(new MaskFormatter(MASK), VALID_CHARS, PLACE_HOLDER);
symbolField.setBorder(new BevelBorder(BevelBorder.LOWERED));
jPanel2.add(portfolioLabel, null);
jPanel2.add(portfolioEditorComboBox, null);
jPanel2.add(startLabel, null);
jPanel2.add(spinnerStart, null);
jPanel2.add(endLabel, null);
jPanel2.add(spinnerEnd, null);
jPanel2.add(symbolLabel, null);
jPanel2.add(symbolField, null);
jPanel2.add(filterButton, null);
jPanel2.setBorder(new BevelBorder(BevelBorder.RAISED));
JToolBar jToolBar = new JToolBar();
jToolBar.setLayout(new BorderLayout());
jToolBar.add(jPanel2, BorderLayout.WEST);
jToolBar.add(jPanel1, BorderLayout.EAST);
m_tableTradelogSummary.setEnabled(false);
m_tableTradelogSummary.setFont(new Font("Monospaced", Font.PLAIN, 12));
JScrollPane jScrollPane = new JScrollPane();
jScrollPane.getViewport().add(m_tableTradelogSummary, BorderLayout.CENTER);
jScrollPane.setBorder(new BevelBorder(BevelBorder.LOWERED));
jScrollPane.addMouseListener(m_tableTradelogSummary);
JPanel jPanel3 = new JPanel(new BorderLayout());
jPanel3.add(jScrollPane, BorderLayout.CENTER);
m_tableTradelogDetail.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
m_tableTradelogDetail.setFont(new Font("Monospaced", Font.PLAIN, 12));
JScrollPane jScrollPane1 = new JScrollPane();
jScrollPane1.getViewport().add(m_tableTradelogDetail, BorderLayout.CENTER);
jScrollPane1.setBorder(new BevelBorder(BevelBorder.LOWERED));
jScrollPane1.addMouseListener(m_tableTradelogDetail);
JPanel jPanel4 = new JPanel(new BorderLayout());
jPanel4.add(jScrollPane1, BorderLayout.CENTER);
JSplitPane jSplitPane1 = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, jPanel3, jPanel4);
jSplitPane1.setOneTouchExpandable(true);
jSplitPane1.setResizeWeight(0.2d);
this.add(jToolBar, BorderLayout.NORTH);
this.add(jSplitPane1, BorderLayout.CENTER);
m_tableTradelogDetail.getSelectionModel().addListSelectionListener(new TradelogDetailTableRowListener());
m_tableTradelogDetail.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
if (null != selectedTradelogDetail) {
transferButton.setTransferObject(selectedTradelogDetail.getIdTradestrategy());
transferButton.doClick();
}
}
}
});
} catch (Exception ex) {
this.setErrorMessage("Error during initialization.", ex.getMessage(), ex);
}
}
public void doSearch() {
try {
ZonedDateTime startDate = TradingCalendar
.getZonedDateTimeFromMilli(((java.util.Date) spinnerStart.getValue()).getTime());
startDate = TradingCalendar.getDateAtTime(startDate, 0, 0, 0);
ZonedDateTime endDate = TradingCalendar
.getZonedDateTimeFromMilli(((java.util.Date) spinnerEnd.getValue()).getTime());
endDate = TradingCalendar.getDateAtTime(endDate, 23, 59, 59);
if (endDate.isBefore(startDate)) {
startDate = TradingCalendar.getDateAtTime(endDate, 0, 0, 0);
spinnerStart.setValue((new Date(startDate)).getDate());
}
String symbol = symbolField.getText().toUpperCase().trim();
if (symbol.length() == 0)
symbol = null;
m_tradelogReport = m_tradePersistentModel.findTradelogReport(this.portfolio, startDate, endDate,
filterButton.isSelected(), symbol, m_lossGainAmt.getMoney().getBigDecimalValue());
this.clearStatusBarMessage();
if (m_tradelogReport.getTradelogDetail().isEmpty()) {
this.setStatusBarMessage("No data found for selected criteria", INFORMATION);
}
m_tradelogDetailModel.setData(m_tradelogReport);
m_tradelogSummaryModel.setData(m_tradelogReport);
RowSorter<?> rsDetail = m_tableTradelogDetail.getRowSorter();
rsDetail.setSortKeys(null);
RowSorter<?> rsSummary = m_tableTradelogSummary.getRowSorter();
rsSummary.setSortKeys(null);
} catch (Exception ex) {
this.setErrorMessage("Error finding Tradingday.", ex.getMessage(), ex);
}
}
/**
* Method itemStateChanged.
*
* @param e
* ItemEvent
* @see java.awt.event.ItemListener#itemStateChanged(ItemEvent)
*/
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
this.portfolio = (Portfolio) ((DAOPortfolio) e.getItem()).getObject();
}
}
/**
* Method stateChanged.
*
* @param e
* ChangeEvent
* @see javax.swing.event.ChangeListener#stateChanged(ChangeEvent)
*/
public void stateChanged(ChangeEvent e) {
}
public void doSaveAs() {
doReadWrite(openFileChooser());
}
public void doOpen() {
}
/**
* This is fired when the menu-bar Contract Details is pressed from the
* Action menu.
*
*/
public void doProperties() {
if (null != selectedTradelogDetail) {
transferButton.setTransferObject(selectedTradelogDetail.getIdTradestrategy());
transferButton.doClick();
}
}
public void doSave() {
doReadWrite(openFileChooser());
}
public void doWindowOpen() {
}
public void doWindowClose() {
}
public void doWindowActivated() {
try {
resetPortfolioComboBox(portfolioEditorComboBox);
if (m_tradelogReport.getTradelogDetail().isEmpty()) {
doSearch();
}
} catch (Exception ex) {
this.setErrorMessage("Error activating window.", ex.getMessage(), ex);
}
}
/**
* Method doWindowDeActivated.
*
* @return boolean
*/
public boolean doWindowDeActivated() {
return true;
}
/**
* Method resetPortfolioComboBox.
*
* @param editorComboBox
* DAODecodeComboBoxEditor
* @throws ValueTypeException
*/
private void resetPortfolioComboBox(final DAODecodeComboBoxEditor editorComboBox) throws ValueTypeException {
Vector<Decode> codesNew = ((new DAOPortfolio()).getCodesDecodes());
DefaultComboBoxModel<Decode> model = new DefaultComboBoxModel<Decode>(codesNew);
editorComboBox.setModel(model);
editorComboBox.setItem(DAOPortfolio.newInstance());
editorComboBox.setRenderer(new DecodeComboBoxRenderer());
}
/**
*/
private class TradelogDetailTableRowListener implements ListSelectionListener {
/**
* Method valueChanged.
*
* @param event
* ListSelectionEvent
* @see javax.swing.event.ListSelectionListener#valueChanged(ListSelectionEvent)
*/
public void valueChanged(ListSelectionEvent event) {
if (!event.getValueIsAdjusting()) {
ListSelectionModel model = (ListSelectionModel) event.getSource();
if (model.getLeadSelectionIndex() > -1) {
selectedTradelogDetail = m_tradelogDetailModel.getData().getTradelogDetail()
.get(m_tableTradelogDetail.convertRowIndexToModel(model.getLeadSelectionIndex()));
}
}
}
}
/**
* Method openFileChooser.
*
* @return String
*/
private String openFileChooser() {
String fileName = null;
ExampleFileFilter filter = new ExampleFileFilter(new String[] { "csv" }, "Portfolio Files");
// Start in the curr dir
if (null == m_csvDefaultDir) {
m_csvDefaultDir = System.getProperty("user.dir");
}
JFileChooser filer1 = new JFileChooser(m_csvDefaultDir);
ExampleFileChooser fileView = new ExampleFileChooser();
filer1.setFileView(fileView);
filer1.addChoosableFileFilter(filter);
filer1.setFileFilter(filter);
filer1.setAccessory(new FilePreviewer(filer1));
int returnVal = 0;
returnVal = filer1.showSaveDialog(this);
// Upon return, getFile() will be null if user cancelled the dialog.
if (returnVal == JFileChooser.APPROVE_OPTION) {
// Non-null file property after return implies user
// selected a file to open.
// Call openFile to attempt to load the text from file into TextArea
if (filer1.getSelectedFile().exists()) {
int result = JOptionPane.showConfirmDialog(this.getFrame(), "File Exists. Do you want to over write ?",
"Warning", JOptionPane.YES_NO_OPTION);
if (result == JOptionPane.YES_OPTION) {
fileName = filer1.getSelectedFile().getPath();
} else {
// cancel
return null;
}
} else {
fileName = filer1.getSelectedFile().getPath();
}
if (!fileName.toUpperCase().endsWith(".CSV")) {
return fileName + ".csv";
}
return fileName;
}
return null;
}
/**
* Method doReadWrite.
*
* @param fileName
* String
*/
private void doReadWrite(String fileName) {
if (null != fileName) {
this.setStatusBarMessage("Saving file " + fileName, BasePanel.INFORMATION);
try (FileWriter fileWriter = new FileWriter(fileName); PrintWriter writer = new PrintWriter(fileWriter);) {
// Write out the header
writer.println(TradelogSummaryTableModel.PERIOD + "," + TradelogSummaryTableModel.BATTING_AVERAGE + ","
+ TradelogSummaryTableModel.SHARPE_RATIO + "," + TradelogSummaryTableModel.GROSS_PL + ","
+ TradelogSummaryTableModel.QUANTITY + "," + TradelogSummaryTableModel.COMMISSION + ","
+ TradelogSummaryTableModel.NET_PL + "," + TradelogSummaryTableModel.WIN_COUNT + ","
+ TradelogSummaryTableModel.WIN_AMOUNT + "," + TradelogSummaryTableModel.LOSS_COUNT + ","
+ TradelogSummaryTableModel.LOSS_AMOUNT + "," + TradelogSummaryTableModel.POSITION_COUNT + ","
+ TradelogSummaryTableModel.CONTRACT_COUNT);
// Write out the lines
if (null != m_tradelogReport) {
for (TradelogSummary tradelogSummary : m_tradelogReport.getTradelogSummary()) {
writer.println(formatTradelogSummaryLine(tradelogSummary));
}
}
// Write out the header
writer.println(TradelogDetailTableModel.DATE + "," + TradelogDetailTableModel.SYMBOL + ","
+ TradelogDetailTableModel.LONGSHORT + "," + TradelogDetailTableModel.TIER + ","
+ TradelogDetailTableModel.MARKET_BIAS + "," + TradelogDetailTableModel.MARKET_BAR + ","
+ TradelogDetailTableModel.STRATEGY + "," + TradelogDetailTableModel.STATUS + ","
+ TradelogDetailTableModel.ACTION + "," + TradelogDetailTableModel.STOP_PRICE + ","
+ TradelogDetailTableModel.STATUS + "," + TradelogDetailTableModel.FILLED_DATE + ","
+ TradelogDetailTableModel.QUANTITY + "," + TradelogDetailTableModel.AVG_FILL_PRICE + ","
+ TradelogDetailTableModel.COMMISION + "," + TradelogDetailTableModel.PROFIT_LOSS);
// Write out the lines
if (null != m_tradelogReport) {
for (TradelogDetail tradelogDetail : m_tradelogReport.getTradelogDetail()) {
writer.println(formatTradelogDetailLine(tradelogDetail));
}
}
writer.flush();
writer.close();
fileWriter.close();
this.setStatusBarMessage("File: " + fileName + " saved.", BasePanel.INFORMATION);
} catch (Exception ex) {
setErrorMessage("Error Reading Writing.", ex.getMessage(), ex);
}
}
}
/**
* Method formatTradelogSummaryLine.
*
* @param tradelogSummary
* TradelogSummary
* @return StringBuffer
*/
private StringBuffer formatTradelogSummaryLine(final TradelogSummary tradelogSummary) {
StringBuffer tradelogLine = new StringBuffer();
tradelogLine
.append((tradelogSummary.getPeriod() == null ? "" : tradelogSummary.getPeriod()) + ","
+ (tradelogSummary.getBattingAverage() == null ? ""
: new Money(tradelogSummary.getBattingAverage()))
+ ","
+ (tradelogSummary.getSimpleSharpeRatio() == null ? ""
: new Money(tradelogSummary.getSimpleSharpeRatio()))
+ ","
+ (tradelogSummary.getGrossProfitLoss() == null ? "" : tradelogSummary.getGrossProfitLoss())
+ "," + (tradelogSummary.getQuantity() == null ? "" : tradelogSummary.getQuantity()) + ","
+ (tradelogSummary.getCommission() == null ? "" : tradelogSummary.getCommission()) + ","
+ (tradelogSummary.getNetProfitLoss() == null ? "" : tradelogSummary.getNetProfitLoss()) + ","
+ (tradelogSummary.getWinCount() == null ? "" : tradelogSummary.getWinCount()) + ","
+ (tradelogSummary.getProfitAmount() == null ? "" : tradelogSummary.getProfitAmount()) + ","
+ (tradelogSummary.getLossCount() == null ? "" : tradelogSummary.getLossCount()) + ","
+ (tradelogSummary.getLossAmount() == null ? "" : tradelogSummary.getLossAmount()) + ","
+ (tradelogSummary.getPositionCount() == null ? "" : tradelogSummary.getPositionCount()) + ","
+ (tradelogSummary.getTradestrategyCount() == null ? ""
: tradelogSummary.getTradestrategyCount()));
return tradelogLine;
}
/**
* Method formatTradelogDetailLine.
*
* @param tradelogDetail
* TradelogDetail
* @return StringBuffer
*/
private StringBuffer formatTradelogDetailLine(final TradelogDetail tradelogDetail) {
StringBuffer tradelogLine = new StringBuffer();
tradelogLine.append(
tradelogDetail.getOpen() + "," + (tradelogDetail.getSymbol() == null ? "" : tradelogDetail.getSymbol())
+ "," + (tradelogDetail.getLongShort() == null ? "" : tradelogDetail.getLongShort()) + ","
+ (tradelogDetail.getTier() == null ? "" : tradelogDetail.getTier()) + ","
+ (tradelogDetail.getMarketBias() == null ? "" : ("\'" + tradelogDetail.getMarketBias())) + ","
+ (tradelogDetail.getMarketBar() == null ? "" : ("\'" + tradelogDetail.getMarketBar())) + ","
+ (tradelogDetail.getName() == null ? "" : tradelogDetail.getName()) + ","
+ (tradelogDetail.getStatus() == null ? "" : tradelogDetail.getStatus()) + ","
+ (tradelogDetail.getAction() == null ? "" : tradelogDetail.getAction()) + ","
+ (tradelogDetail.getStopPrice() == null ? "" : tradelogDetail.getStopPrice()) + ","
+ (tradelogDetail.getOrderStatus() == null ? "" : tradelogDetail.getOrderStatus()) + ","
+ (tradelogDetail.getFilledDate() == null ? "" : tradelogDetail.getFilledDate()) + ","
+ (tradelogDetail.getQuantity() == null ? "" : tradelogDetail.getQuantity()) + ","
+ (tradelogDetail.getAverageFilledPrice() == null ? "" : tradelogDetail.getAverageFilledPrice())
+ "," + (tradelogDetail.getCommission() == null ? "" : tradelogDetail.getCommission()) + ","
+ (tradelogDetail.getProfitLoss() == null ? "" : tradelogDetail.getProfitLoss()));
return tradelogLine;
}
}