//////////////////////////Trade.aspx///////////////////////////////////////////
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using COM.TwoForBoth.CalcLib;
using SoftwareFX.ChartFX;
using SoftwareFX.ChartFX.Annotation;
using SoftwareFX.ChartFX.Internet.Server;
using SoftwareFX.ChartFX.Internet.Server.GalleryObj;
namespace CalcLibTest
{
///
/// Summary description for History.
///
public class History : System.Web.UI.Page
{
protected System.Web.UI.WebControls.Label lblSymbolNotFound;
protected System.Web.UI.WebControls.Button btnOK;
protected System.Web.UI.WebControls.TextBox txtSymbol;
protected System.Web.UI.WebControls.Label Label1;
protected Chart Chart1;
protected System.Web.UI.WebControls.Label Label2;
protected System.Web.UI.WebControls.Label Label3;
protected System.Web.UI.WebControls.TextBox txtStartingBalance;
protected System.Web.UI.WebControls.Label lblInvalidValue;
protected System.Web.UI.WebControls.DataGrid DataGridResults;
private static string tableName = "myTable";
private static string colDate = "Date";
private static string colPosition = "Position";
private static string colSize = "Size";
private static string colPrice = "Price";
private static string colCost = "Cost";
private static string colBalance = "Balance";
private DataTable myDataTable = new DataTable(tableName);
private double [] opens;
private double [] highs;
private double [] lows;
private double [] closes;
private DateTime [] dates;
private DateTime startDate = new DateTime(2002, 1, 2);
private DateTime endDate = new DateTime(2002, 12, 31);
//Need at least 26 trading days previous to startDate for EMA26
private DateTime calcStartDate = new DateTime(2001, 11, 1);
private Signal signal;
protected System.Web.UI.WebControls.Label Label4;
protected System.Web.UI.WebControls.Label Label5;
protected System.Web.UI.WebControls.Label lblPandL;
protected System.Web.UI.WebControls.Calendar CalendarStart;
protected System.Web.UI.WebControls.Calendar CalendarEnd;
private MACD macd;
private void Page_Load(object sender, System.EventArgs e)
{
// Put user code to initialize the page here
Chart1.OpenData(SoftwareFX.ChartFX.COD.Values, 6, 0);
Chart1.CloseData(SoftwareFX.ChartFX.COD.Values);
buildTable();
CalendarStart.TodaysDate = startDate;
CalendarEnd.TodaysDate = endDate;
}
#region Web Form Designer generated code
override protected void OnInit(EventArgs e)
{
//
// CODEGEN: This call is required by the ASP.NET Web Form Designer.
//
InitializeComponent();
base.OnInit(e);
}
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
private void InitializeComponent()
{
this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
this.CalendarStart.SelectionChanged += new System.EventHandler(this.CalendarStart_SelectionChanged);
this.CalendarEnd.SelectionChanged += new System.EventHandler(this.CalendarEnd_SelectionChanged);
this.Load += new System.EventHandler(this.Page_Load);
}
#endregion
private void btnOK_Click(object sender, System.EventArgs e)
{
double val;
double balance;
//Initially clear the chart of any extensions
Chart1.Extensions.Clear();
//Interested in closes for all trading dates in 2002
startDate = CalendarStart.SelectedDate;
endDate = CalendarEnd.SelectedDate;
//Need at least 26 trading days previous to startDate for EMA26
calcStartDate = startDate.AddMonths(-2);
//Get historical data for plotting
HistoryArray ha = new HistoryArray(txtSymbol.Text, startDate, endDate);
if (ha.getSize() > 0)
{
//Get historical data for calculations
HistoryArray preArray = new HistoryArray(txtSymbol.Text, calcStartDate, endDate);
double [] preCloses = preArray.getCloses();
DateTime [] preDates = preArray.getDates();
lblSymbolNotFound.Visible = false;
lblInvalidValue.Visible = false;
try
{
balance = Double.Parse(txtStartingBalance.Text);
}
catch
{
lblInvalidValue.Visible = true;
balance = 10000.0;
}
opens = ha.getOpens();
highs = ha.getHighs();
lows = ha.getLows();
closes = ha.getCloses();
dates = ha.getDates();
macd = new MACD(preCloses, preDates);
macd.doCalc();
signal = new Signal(preCloses, preDates);
signal.doCalc();
//Analyze when to trade using the MACD
Analyzer an = new Analyzer(macd, ha);
an.doCalc();
int index = an.getOutputPointIndexByDate(startDate);
int endIndex = an.getDataSize();
//Create a portfolio and trade based on the MACD rules results returned by tge analyzer
Portfolio p = new Portfolio(balance);
for (int i = index; i != endIndex; i++)
{
Data d = an.getOutputPointByIndex(i);
if (d != null)
{
if (d.status == (int)Analyzer.TradeIndicators.Buy || d.status == (int)Analyzer.TradeIndicators.Sell)
{
p.add(d.date, (int)d.status, d.val);
}
}
}
//In case last trade was a buy
double finalPrice = ha.getCloseByDate(endDate);
p.forceLastSell(endDate, finalPrice);
fillTable(p, balance, startDate, endDate, ha.getCloseByDate(endDate));
int nPoints = opens.Length;
string title = txtSymbol.Text + " " + startDate.ToShortDateString() + " - " + endDate.ToShortDateString();
Chart1.Titles[0].Text = title;
Chart1.AxisX.Title.Text = "Day Number";
Chart1.AxisY.Title.Text = "Price";
//Open the Values channels specifying 5 series
//Open, High, Low, Close and MACD and "nPoints" points
Chart1.OpenData(SoftwareFX.ChartFX.COD.Values, 6, nPoints);
//Set Data
DateTime currentDate = new DateTime(2002, 1, 1);
for (int j = 0; j != nPoints; j++)
{
if (ha.hasDate(currentDate))
{
Chart1.Value[(int)SoftwareFX.ChartFX.OHLC.LOW, j] = lows[j];
Chart1.Value[(int)SoftwareFX.ChartFX.OHLC.OPEN, j] = opens[j];
Chart1.Value[(int)SoftwareFX.ChartFX.OHLC.CLOSE, j] = closes[j];
Chart1.Value[(int)SoftwareFX.ChartFX.OHLC.HIGH, j] = highs[j];
//fifth series will have the moving avg
Data point = macd.getOutputPointByDate(currentDate);
if (point != null)
{
val = point.val;
if (val != Data.Undefined && val != Data.Invalid)
{
Chart1.Value[4, j] = val;
}
else
{
Chart1.Value[4, j] = SoftwareFX.ChartFX.Internet.Server.Chart.Hidden;
}
}
else
{
Chart1.Value[4, j] = SoftwareFX.ChartFX.Internet.Server.Chart.Hidden;
}
point = signal.getOutputPointByDate(currentDate);
if (point != null)
{
val = point.val;
if (val != Data.Undefined && val != Data.Invalid)
{
Chart1.Value[5, j] = val;
}
else
{
Chart1.Value[5, j] = SoftwareFX.ChartFX.Internet.Server.Chart.Hidden;
}
}
else
{
Chart1.Value[5, j] = SoftwareFX.ChartFX.Internet.Server.Chart.Hidden;
}
}
currentDate = currentDate.AddDays(1);
}
//Close the Values channel
Chart1.CloseData(SoftwareFX.ChartFX.COD.Values);
Chart1.RecalcScale();
//Now use the gallery property in the series object to create a line for the moving avg
SoftwareFX.ChartFX.Internet.Server.SeriesAttributes series = Chart1.Series[4];
series.Gallery = SoftwareFX.ChartFX.Gallery.Lines;
series.MarkerShape = SoftwareFX.ChartFX.MarkerShape.None;
SoftwareFX.ChartFX.Internet.Server.SeriesAttributes series2 = Chart1.Series[5];
series2.Gallery = SoftwareFX.ChartFX.Gallery.Lines;
series2.MarkerShape = SoftwareFX.ChartFX.MarkerShape.None;
Chart1.SerLegBox = true;
Chart1.Series[(int)SoftwareFX.ChartFX.OHLC.LOW].Legend = "Low";
Chart1.Series[(int)SoftwareFX.ChartFX.OHLC.OPEN].Legend = "Open";
Chart1.Series[(int)SoftwareFX.ChartFX.OHLC.CLOSE].Legend = "Close";
Chart1.Series[(int)SoftwareFX.ChartFX.OHLC.HIGH].Legend = "High";
Chart1.Series[4].Legend = "MACD";
Chart1.Series[5].Legend = "Signal";
annotateChart(p, ha);
Chart1.UserLegendBoxObj.Docked = Docked.Right;
UserLegendBoxItem item1 = Chart1.UserLegendBoxObj.Item[0];
item1.Label = "Buy";
item1.Color = Color.Green;
item1.MarkerShape = MarkerShape.Circle;
item1.BorderEffect = BorderEffect.None;
item1.Border.Color = Color.Black;
UserLegendBoxItem item2 = Chart1.UserLegendBoxObj.Item[1];
item2.Label = "Sell";
item2.Color = Color.Red;
item2.MarkerShape = MarkerShape.Circle;
item2.BorderEffect = BorderEffect.None;
item2.Border.Color = Color.Black;
Chart1.UserLegendBox = true;
lblPandL.Visible = true;
}
else
{
Chart1.SerLegBox = false;
Chart1.OpenData(SoftwareFX.ChartFX.COD.Values, 4, 0);
Chart1.ClearData(SoftwareFX.ChartFX.ClearDataFlag.AllData);
Chart1.CloseData(SoftwareFX.ChartFX.COD.Values);
lblSymbolNotFound.Visible = true;
Chart1.UserLegendBox = false;
}
}
///
/// Constructs the table columns
///
private void buildTable()
{
DataColumn myDataColumn;
// Date Column
myDataColumn = new DataColumn();
myDataColumn.ColumnName = colDate;
myDataColumn.AutoIncrement = false;
myDataColumn.ReadOnly = true;
myDataColumn.Unique = false;
// Add the column to the table.
myDataTable.Columns.Add(myDataColumn);
// Position Column
myDataColumn = new DataColumn();
myDataColumn.ColumnName = colPosition;
myDataColumn.AutoIncrement = false;
myDataColumn.ReadOnly = true;
myDataColumn.Unique = false;
// Add the column to the table.
myDataTable.Columns.Add(myDataColumn);
// Size Column
myDataColumn = new DataColumn();
myDataColumn.ColumnName = colSize;
myDataColumn.AutoIncrement = false;
myDataColumn.ReadOnly = true;
myDataColumn.Unique = false;
// Add the column to the table.
myDataTable.Columns.Add(myDataColumn);
// Price
myDataColumn = new DataColumn();
myDataColumn.ColumnName = colPrice;
myDataColumn.AutoIncrement = false;
myDataColumn.ReadOnly = true;
myDataColumn.Unique = false;
// Add the column to the table.
myDataTable.Columns.Add(myDataColumn);
// Cost Column
myDataColumn = new DataColumn();
myDataColumn.ColumnName = colCost;
myDataColumn.AutoIncrement = false;
myDataColumn.ReadOnly = true;
myDataColumn.Unique = false;
// Add the column to the table.
myDataTable.Columns.Add(myDataColumn);
// Balance Column
myDataColumn = new DataColumn();
myDataColumn.ColumnName = colBalance;
myDataColumn.AutoIncrement = false;
myDataColumn.ReadOnly = true;
myDataColumn.Unique = false;
// Add the column to the table.
myDataTable.Columns.Add(myDataColumn);
}
///
/// Draws red circles for sell points and green circles for buy points
///
/// Portfolio object
/// HistoryArray object
private void annotateChart(Portfolio p, HistoryArray ha)
{
int priorIndex = -1;
AnnotationX annotx = new AnnotationX();
Chart1.Extensions.Add(annotx);
int endIndex = p.getSize();
for (int i = 0; i != endIndex; i++)
{
//Get the trade
Trade t = p.getReportLine(i);
string priceString = t.PriceString;
double price = Double.Parse(priceString);
DateTime date = DateTime.Parse(t.DateString);
int dateIndex = ha.getIndexFromDate(date);
string tradeString = t.PositionString;
if (dateIndex != priorIndex)
{
AnnotationCircle circle = new AnnotationCircle();
annotx.List.Add(circle);
// Configure the annotation object
//if (t.PositionString.Equals("LONG"))
if (t.Signal == (int)Trade.Signals.buy)
{
circle.Color = System.Drawing.Color.Green;
}
else
{
circle.Color = System.Drawing.Color.Red;
}
circle.Height = 10;
circle.Width = 10;
circle.Attach(dateIndex, price);
circle.Refresh();
priorIndex = dateIndex;
}
}
}
///
/// Builds the P&L Report
///
/// Portfolio object
/// Beginning balance (should really get this from the portfolio object
/// Starting period of trading
/// Ending period of trading
/// Final price for endDate
private void fillTable(Portfolio p, double startingBalance, DateTime startDate, DateTime endDate, double endingPrice)
{
DataRow myDataRow;
// Instantiate the DataSet variable.
DataSet myDataSet = new DataSet();
// Add the new DataTable to the DataSet.
myDataSet.Tables.Add(myDataTable);
//First line is starting balance
myDataRow = myDataTable.NewRow();
myDataRow[colDate] = startDate.ToShortDateString();
myDataRow[colPosition] = "START";
myDataRow[colSize] = "NA";
myDataRow[colPrice] = "NA";
myDataRow[colCost] = "NA";
myDataRow[colBalance] = String.Format("{0:c}", startingBalance);
myDataTable.Rows.Add(myDataRow);
// Create new DataRow objects and add them to the DataTable
int endIndex = p.getSize();
for (int i = 0; i != endIndex; i++)
{
//Get the trade
Trade t = p.getReportLine(i);
myDataRow = myDataTable.NewRow();
myDataRow[colDate] = t.DateString;
myDataRow[colPosition] = t.PositionString;
myDataRow[colSize] = t.SizeString;
myDataRow[colPrice] = t.PriceString;
myDataRow[colCost] = t.CostString;
myDataRow[colBalance] = t.BalanceString;
myDataTable.Rows.Add(myDataRow);
}
//Ending Balance line
myDataRow = myDataTable.NewRow();
myDataRow[colDate] = endDate.ToShortDateString();
myDataRow[colPosition] = "END";
myDataRow[colSize] = "NA";
myDataRow[colPrice] = "NA";
myDataRow[colCost] = "NA";
myDataRow[colBalance] = p.EndingBalanceString;
myDataTable.Rows.Add(myDataRow);
//Ending Balance line
myDataRow = myDataTable.NewRow();
myDataRow[colDate] = endDate.ToShortDateString();
myDataRow[colPosition] = "P&L";
myDataRow[colSize] = "NA";
myDataRow[colPrice] = "NA";
myDataRow[colCost] = "NA";
myDataRow[colBalance] = p.PL;
myDataTable.Rows.Add(myDataRow);
// Instruct the DataGrid to bind to the DataSet, with the
// ParentTable as the topmost DataTable.
DataGridResults.DataSource = myDataSet;
DataGridResults.DataBind();
}
private void CalendarStart_SelectionChanged(object sender, System.EventArgs e)
{
CalendarStart.TodaysDate = CalendarStart.SelectedDate;
}
private void CalendarEnd_SelectionChanged(object sender, System.EventArgs e)
{
CalendarEnd.TodaysDate = CalendarEnd.SelectedDate;
}
}
}
////////////CALCLIB.CS///////////////////////////////////////////////////////
using System;
using System.Collections;
///Calc library Copyright Stuart Blavatnik 2003
///This library calculates the following formulas:
///
/// 1) Simple Moving Average (aka SMA)
/// 2) Exponential Moving Average (aka EMA)
/// 3) Moving average convergence divergence (aka MACD)
/// 4) Signal (aka 9 period EMA of MACD)
namespace COM.TwoForBoth.CalcLib
{
///
/// Data represents a data point. The point is made up of a value,
/// a date and a status (whether the value was initialized or not)
///
public class Data
{
private double v; //Value
private DateTime d; //Date associated with Value
private int s; //status of value -- default to Undefined
public static int Undefined //Point was not defined yet
{
get
{
return -1;
}
}
public static int Initialized //Point was initialized
{
get
{
return 1;
}
}
public static double Invalid //Invalid Value
{
get
{
return -9999999.9999;
}
}
public double val
{
get
{
return v;
}
set
{
v = value;
status = Initialized;
}
}
public DateTime date
{
get
{
return d;
}
set
{
d = value;
}
}
public int status
{
get
{
return s;
}
set
{
s = value;
}
}
//Default Constructor
public Data()
{
v = 0.0;
d = new DateTime(1980, 1, 1, 0, 0, 0);
s = Undefined;
}
public Data(double val, DateTime date)
{
v = val;
d = date;
s = Initialized;
}
public Data(double val)
{
v = val;
s = Initialized;
}
}
///
/// Calc is a base class for other calculation formulas
///
public abstract class Calc
{
protected Data [] input;
protected Data [] output;
public enum status
{
ok,
bad
}
///
/// Constructor taking values and dates
///
/// Values
/// Dates
public void initValues(double [] values, DateTime [] dates)
{
input = new Data[values.Length];
output = new Data[values.Length];
for (int i = 0; i < input.Length; i++)
{
input[i] = new Data(values[i], dates[i]);
output[i] = new Data();
output[i].date = dates[i];
}
}
///
/// Constructor taking only values
///
/// Values
public void initValues(double [] values)
{
input = new Data[values.Length];
output = new Data[values.Length];
for (int i = 0; i < output.Length; i++)
{
input[i] = new Data(values[i]);
output[i] = new Data();
}
}
abstract public int doCalc();
public Data [] getResults()
{
return output;
}
///
/// Retrieves a data point from the output array given an index into the output array
///
/// Index of array (value between 0 and array.size
/// Data object or null
public Data getOutputPointByIndex(int index)
{
Data retval = null;
if (index >= 0 && index < output.Length)
{
retval = output[index];
}
return retval;
}
///
/// Retrieves a data point given a date
///
/// DateTime object
/// Data object or null
public Data getOutputPointByDate(DateTime date)
{
Data retval = null;
for (int i = 0; i < output.Length; i++)
{
if (output[i].status != Data.Undefined && date.Equals(output[i].date))
{
retval = output[i];
break;
}
}
return retval;
}
///
/// Retrieves the first output point where the data status is not Undefined
///
/// Point, or null
public Data getFirstDefinedPoint()
{
Data retval = null;
for (int i = 0; i < output.Length; i++)
{
if (output[i].status != Data.Undefined)
{
retval = output[i];
break;
}
}
return retval;
}
///
/// Retrieves the first output point where the data status is not Undefined
///
/// Point, or null
public int getFirstDefinedPointIndex()
{
int retval = -1;
for (int i = 0; i < output.Length; i++)
{
if (output[i].status != Data.Undefined)
{
retval = i;
break;
}
}
return retval;
}
public int getOutputLength()
{
return output.Length;
}
public int getInputLength()
{
return input.Length;
}
}
///
/// Class: SimpleMovingAverage
///
/// A simple moving average is formed by finding the average price
/// of a security over a set number of periods. Most often, the
/// closing price is used to compute the moving average. For
/// example: a 5-day moving average would be calculated by adding
/// the closing prices for the last 5 days and dividing the total
/// by 5.
/// example:
/// 10 + 11 + 12 + 13 + 14 = 60
/// 60 / 5 = 12
///
/// A moving average moves because as the newest period is added,
/// the oldest period is dropped. If the next closing price in the
/// average is 15, then this new period would be added and the
/// oldest day, which is 10, would be dropped. The new 5-day moving
/// average would be calculated as follows:
///
/// 11 + 12 + 13 + 14 + 15 = 65
/// 65 / 5 = 13
///
/// Over the last 2 days, the moving average moved from 12 to 13.
/// As new days are added, the old days will be subtracted and
/// the moving average will continue to move over time.
///
public class SimpleMovingAverage : Calc
{
private int period; //Number of values to take
public int Period
{
get
{
return period;
}
}
public SimpleMovingAverage(double [] values, DateTime [] dates, int periods)
{
initValues(values, dates);
period = periods;
}
///
/// Function: doCalc()
///
/// Parameters: None
///
/// Description: Calculates a simple moving average
///
public override int doCalc()
{
int retval = (int)Calc.status.ok;
for (int j = 0; j < input.Length; j++)
{
if (j >= period - 1 && input[j].val != Data.Invalid) //0 based
{
output[j].val = getSum(j, period) / period;
}
else
{
output[j].val = 0;
output[j].status = Data.Undefined;
}
}
return retval;
}
public Data getOutputPoint(int index)
{
//Add check here for index
return output[index];
}
private double getSum(int start, int length)
{
double retval = 0.0;
for (int i = start, j = 0; j < (length); i--, j++)
{
if (input[i].val != Data.Invalid)
{
retval += input[i].val;
}
}
return retval;
}
}
///
/// Class: ExponentialMovingAverage
///
/// In order to reduce the lag in simple moving averages, technicians
/// sometimes use exponential moving averages, or exponentially
/// weighted moving averages. Exponential moving averages reduce
/// the lag by applying more weight to recent prices relative to
/// older prices. The weighting applied to the most recent price
/// depends on the length of the moving average. The shorter the
/// exponential moving average is, the more weight that will be
/// applied to the most recent price. For example: a 10-period
/// exponential moving average weighs the most recent price 18.18%
/// and a 20-period exponential moving average weighs the most
/// recent price 9.52%. The method for calculating the exponential
/// moving average is fairly complicated. The important thing to
/// remember is that the exponential moving average puts more
/// weight on recent prices. As such, it will react quicker to
/// recent price changes than a simple moving average.
///
/// Exponential Moving Average Calculation
/// The formula for an exponential moving average is:
/// X = (K x (C - P)) + P
/// X = Current EMA
/// C = Current Price
/// P = Previous period's EMA*
/// K = Smoothing constant
/// (*A SMA is used for first period's calculation)
///
/// The smoothing constant applies the appropriate weighting to the
/// most recent price relative to the previous exponential moving
/// average. The formula for the smoothing constant is:
///
/// K = 2/(1+N)
/// N = Number of periods for EMA
/// For a 10-period EMA, the smoothing constant would be .1818.
///
public class ExponentialMovingAverage : Calc
{
private int period; //Number of values to take
private SimpleMovingAverage sma;
private double smoothingConstant;
public int Period
{
get
{
return period;
}
}
public ExponentialMovingAverage(double [] values, DateTime [] dates, int periods)
{
initValues(values, dates);
period = periods;
sma = new SimpleMovingAverage(values, dates, periods);
smoothingConstant = calcSmoothingConstant(period);
}
private double calcSmoothingConstant(int period)
{
double k = (double) 2 / ( 1 + period);
return k;
}
public override int doCalc()
{
int retval = (int)Calc.status.ok;
// First calculate the SMA for the use for the first point
sma.doCalc();
//Get first defined data point
//1/21/03 -- now first check the value of sma.getFirstDefinedPoint()
Data d = sma.getFirstDefinedPoint();
if (d != null)
{
double simpleMovingAverage = sma.getFirstDefinedPoint().val;
/// X = (K x (C - P)) + P
/// X = Current EMA
/// C = Current Price
/// P = Previous period's EMA*
/// K = Smoothing constant
/// (*A SMA is used for first period's calculation)
bool first = true;
for (int j = 0; j < input.Length; j++)
{
if (j >= period - 1 && input[j].val != Data.Invalid) //0 based
{
if (first)
{
output[j].val = simpleMovingAverage;
first = false;
}
else
{
output[j].val = (smoothingConstant * (input[j].val - output[j - 1].val)) + output[j - 1].val;
}
}
else
{
output[j].val = 0;
output[j].status = Data.Undefined;
}
}
}
else
{
retval = (int)Calc.status.bad;
}
return retval;
}
}
///
/// Class MACD -- Simply the difference between the 12 and 26 day moving averages
///
public class MACD : Calc
{
private ExponentialMovingAverage EMA12;
private ExponentialMovingAverage EMA26;
public MACD(double [] values, DateTime [] dates)
{
initValues(values, dates);
EMA12 = new ExponentialMovingAverage(values, dates, 12);
EMA26 = new ExponentialMovingAverage(values, dates, 26);
EMA12.doCalc();
EMA26.doCalc();
}
///
/// Subtracts the values of the 12 DAY EMA from the 26 DAY EMA
///
public override int doCalc()
{
int retval = (int)Calc.status.ok;
for (int i = 0; i < input.Length; i++)
{
//Get the points for the 12 and 26 day EMA's using date from 12 Day
Data point = EMA26.getOutputPointByDate(EMA12.getOutputPointByIndex(i).date);
if (point != null)
{
output[i].date = point.date;
output[i].val = EMA12.getOutputPointByIndex(i).val - point.val;
}
else
{
output[i].status = Data.Undefined;
}
}
return retval;
}
}
///
/// Signal can also be thought of as the 9 period EMA of the MACD
///
public class Signal : Calc
{
private MACD macd;
private DateTime[] dates;
public Signal(double [] values, DateTime [] dates)
{
initValues(values, dates);
this.dates = dates;
macd = new MACD(values, dates);
macd.doCalc();
}
public override int doCalc()
{
int retval = (int)Calc.status.ok;
Data [] macdResults = macd.getResults();
double [] macdVals = new double[macdResults.Length];
for (int i = 0; i < macdResults.Length; i++)
{
if (macdResults[i].status != Data.Undefined)
{
macdVals[i] = macdResults[i].val;
}
else
{
macdVals[i] = Data.Invalid;
}
}
ExponentialMovingAverage ema9 = new ExponentialMovingAverage(macdVals, dates, 9);
ema9.doCalc();
for (int i = 0; i < ema9.getResults().Length; i++)
{
Data point = ema9.getOutputPointByIndex(i);
if (point != null && point.status != Data.Undefined && point.val != Data.Invalid)
{
output[i].val = point.val;
}
}
return retval;
}
}
///
/// Represents an array of historical points for a given instrument
///
public class HistoryArray
{
private ArrayList HistoricalDataPoints;
private QuotesPlusWrapper qpw;
public HistoryArray(string symbol, DateTime from, DateTime to)
{
DateTime date;
double open;
double high;
double low;
double close;
HistoricalDataPoints = new ArrayList();
qpw = new QuotesPlusWrapper(symbol);
if (qpw.symbolExists())
{
int fromIndex = qpw.getDaysBack(from);
int toIndex = qpw.getDaysBack(to);
if (fromIndex != -1 && toIndex != -1)
{
for (int i = fromIndex; i <= toIndex; i++)
{
if ((open = qpw.getOpen(i)) > 0.0)
{
date = qpw.getDate(i);
high = qpw.getHigh(i);
low = qpw.getLow(i);
close = qpw.getClose(i);
HistoricalDataPoint hdp = new HistoricalDataPoint(date, open, high, low, close);
HistoricalDataPoints.Add(hdp);
}
}
}
}
}
public int getSize()
{
return HistoricalDataPoints.Count;
}
public double [] getOpens()
{
double [] retval = new double[HistoricalDataPoints.Count];
for (int i = 0; i < HistoricalDataPoints.Count; i++)
{
HistoricalDataPoint hdp = (HistoricalDataPoint)HistoricalDataPoints[i];
retval[i] = hdp.Open;
}
return retval;
}
public double [] getHighs()
{
double [] retval = new double[HistoricalDataPoints.Count];
for (int i = 0; i < HistoricalDataPoints.Count; i++)
{
HistoricalDataPoint hdp = (HistoricalDataPoint)HistoricalDataPoints[i];
retval[i] = hdp.High;
}
return retval;
}
public double [] getLows()
{
double [] retval = new double[HistoricalDataPoints.Count];
for (int i = 0; i < HistoricalDataPoints.Count; i++)
{
HistoricalDataPoint hdp = (HistoricalDataPoint)HistoricalDataPoints[i];
retval[i] = hdp.Low;
}
return retval;
}
public double [] getCloses()
{
double [] retval = new double[HistoricalDataPoints.Count];
for (int i = 0; i < HistoricalDataPoints.Count; i++)
{
HistoricalDataPoint hdp = (HistoricalDataPoint)HistoricalDataPoints[i];
retval[i] = hdp.Close;
}
return retval;
}
public DateTime [] getDates()
{
DateTime [] retval = new DateTime[HistoricalDataPoints.Count];
for (int i = 0; i < HistoricalDataPoints.Count; i++)
{
HistoricalDataPoint hdp = (HistoricalDataPoint)HistoricalDataPoints[i];
retval[i] = hdp.Date;
}
return retval;
}
public bool hasDate(DateTime date)
{
bool retval = false;
for (int i = 0; i < HistoricalDataPoints.Count; i++)
{
HistoricalDataPoint hdp = (HistoricalDataPoint)HistoricalDataPoints[i];
if (hdp.Date.CompareTo(date) == 0)
{
retval = true;
break;
}
}
return retval;
}
public double getCloseByDate(DateTime date)
{
double retval = Data.Invalid;
for (int i = 0; i < HistoricalDataPoints.Count; i++)
{
HistoricalDataPoint hdp = (HistoricalDataPoint)HistoricalDataPoints[i];
if (hdp.Date.CompareTo(date) == 0)
{
retval = hdp.Close;
break;
}
}
return retval;
}
public int getIndexFromDate(DateTime date)
{
int retval = -1;
for (int i = 0; i < HistoricalDataPoints.Count; i++)
{
HistoricalDataPoint hdp = (HistoricalDataPoint)HistoricalDataPoints[i];
if (hdp.Date.CompareTo(date) == 0)
{
retval = i;
break;
}
}
return retval;
}
}
///
/// Represents one point (open, high, low, close and date)
///
class HistoricalDataPoint
{
private double open;
private double high;
private double low;
private double close;
private DateTime date;
public HistoricalDataPoint(DateTime date, double open, double high, double low, double close)
{
this.date = date;
this.open = open;
this.high = high;
this.low = low;
this.close = close;
}
public double Open
{
get
{
return open;
}
set
{
open = value;
}
}
public double High
{
get
{
return high;
}
set
{
high = value;
}
}
public double Low
{
get
{
return low;
}
set
{
low = value;
}
}
public double Close
{
get
{
return close;
}
set
{
close = value;
}
}
public DateTime Date
{
get
{
return date;
}
set
{
date = value;
}
}
}
///
/// Wraps the data access functions
///
class QuotesPlusWrapper
{
private QuotesPlus.Price2 qpPrice2 = new QuotesPlus.Price2Class();
public QuotesPlusWrapper(string symbol)
{
//qpPrice2.RawData = 0; //ID_RAWDATA -- 0 or 1 based on check box
//qpPrice2.UseHolidays = 1; //ID_HOL -- 0 or 1 based on check box
qpPrice2.Symbol = symbol;
}
public bool symbolExists()
{
bool retval = true;
object o = qpPrice2.Open(-1);
if (o.ToString().Equals("0"))
{
retval = false;
}
return retval;
}
///
/// getDaysBack() retrieves the number of days back in the QP2 database a
/// particular date is relative to the 0 point in the database (note QP2 days
/// are from 0 to -3000)
///
/// QuotePlus Price 2 object
/// Date being searched for
/// Number of days to go back
public int getDaysBack(DateTime date)
{
object o;
int retval = -1;
DateTime d;
for (int i = 0; i != -3000; i--)
{
o = qpPrice2.Date(i);
try
{
d = (DateTime)o;
if (d.Equals(date))
{
retval = i;
break;
}
}
catch
{
}
}
return retval;
}
public double getOpen(int daynum)
{
return Double.Parse(qpPrice2.Open(daynum).ToString());
}
public double getHigh(int daynum)
{
return Double.Parse(qpPrice2.High(daynum).ToString());
}
public double getLow(int daynum)
{
return Double.Parse(qpPrice2.Low(daynum).ToString());
}
public double getClose(int daynum)
{
return Double.Parse(qpPrice2.Close(daynum).ToString());
}
public DateTime getDate(int daynum)
{
return DateTime.Parse(qpPrice2.Date(daynum).ToString());
}
}
public class Analyzer : Calc
{
private MACD macd;
private HistoryArray ha;
//private Data [] output;
public enum TradeIndicators
{
Buy,
Sell,
Neutral
}
public Analyzer(MACD macd, HistoryArray ha)
{
this.macd = macd;
this.ha = ha;
output = new Data[macd.getOutputLength()]; //Might want to use HA.length here
for (int i = 0; i < output.Length; i++)
{
output[i] = new Data();
}
}
///
/// Does the MACD analysis
///
public override int doCalc()
{
int retval = (int)Calc.status.ok;
int currentIndex = macd.getFirstDefinedPointIndex();
if (currentIndex != -1)
{
int endIndex = macd.getOutputLength();
double previousValue = 0; //Assume neutral
for (int j = 0; j < currentIndex; j++)
{
Data point = macd.getOutputPointByIndex(j);
output[j].date = point.date;
output[j].val = Data.Invalid;
output[j].status = (int)TradeIndicators.Neutral;
}
for (int i = currentIndex; i < endIndex; i++)
{
Data point = macd.getOutputPointByIndex(i);
if (point.status != Data.Invalid && point.status != Data.Undefined)
{
if (point.val < 0.0 && previousValue < 0.0)
{
output[i].date = point.date;
output[i].val = ha.getCloseByDate(point.date);
output[i].status = (int)TradeIndicators.Neutral;
}
else if (point.val < 0.0 && previousValue >= 0.0)
{
output[i].date = point.date;
output[i].val = ha.getCloseByDate(point.date);
output[i].status = (int)TradeIndicators.Sell;
}
else if (point.val > 0.0 && previousValue > 0.0)
{
output[i].date = point.date;
output[i].val = ha.getCloseByDate(point.date);
output[i].status = (int)TradeIndicators.Neutral;
}
else if (point.val > 0.0 && previousValue <= 0.0)
{
output[i].date = point.date;
output[i].val = ha.getCloseByDate(point.date);
output[i].status = (int)TradeIndicators.Buy;
}
previousValue = point.val;
}
}
}
else
{
retval = (int)Calc.status.bad;
}
return retval;
}
///
/// Retrieves an index given a date
///
/// DateTime object
/// Index >= 0
public int getOutputPointIndexByDate(DateTime date)
{
int retval = -1;
for (int i = 0; i < output.Length; i++)
{
if (output[i].status != Data.Undefined && date.Equals(output[i].date))
{
retval = i;
break;
}
}
return retval;
}
public int getDataSize()
{
return output.Length;
}
}
///
/// Individual trade
///
public class Trade
{
public enum Signals
{
buy,
sell
}
public enum Positions
{
buyLong,
sellLong,
sellShort,
coverShort
}
public Trade(DateTime date, int signal, int position, double size, double price, double balance)
{
this.date = date;
this.signal = signal;
this.position = position;
this.size = size;
this.price = price;
this.cost = size * price;
this.balance = balance;
}
private DateTime date;
private int signal;
private int position;
private double size;
private double price; //share price
private double cost; //transaction cost
private double balance;
public double Size
{
get
{
return size;
}
}
public double Price
{
get
{
return price;
}
}
public double Cost
{
get
{
return cost;
}
}
public int Signal
{
get
{
return signal;
}
}
public int Position
{
get
{
return position;
}
}
public String DateString
{
get
{
return date.ToShortDateString();
}
}
public string PositionString
{
get
{
if (position == (int)Positions.buyLong)
{
return "BUY LONG";
}
else if (position == (int)Positions.coverShort)
{
return "COVER SHORT";
}
else if (position == (int)Positions.sellLong)
{
return "SELL LONG";
}
else
{
return "SELL SHORT";
}
}
}
public string SizeString
{
get
{
return size.ToString();
}
}
public string PriceString
{
get
{
return String.Format("{0:c}", price.ToString());
}
}
public string CostString
{
get
{
return String.Format("{0:c}", cost);
}
}
public string BalanceString
{
get
{
return String.Format("{0:c}", balance);
}
}
}
public class Portfolio
{
private ArrayList trades = new ArrayList();
private double balance;
private double currentShares;
private double startingBalance;
private double endingBalance;
private double pl;
public Portfolio(double balance)
{
this.balance = balance;
startingBalance = balance;
endingBalance = balance;
pl = 0.0;
currentShares = 0.0;
}
public void forceLastSell(DateTime date, double price)
{
double size;
if (currentShares > 0.0)
{
size = currentShares;
balance += size * price;
//Add the trade
trades.Add(new Trade(date, (int)Trade.Signals.sell, (int)Trade.Positions.sellLong, size, price, balance));
currentShares = 0.0;
}
else if (currentShares < 0.0)
{
//Get previous size
size = ((Trade)trades[trades.Count - 1]).Size;
double previousPrice = ((Trade)trades[trades.Count - 1]).Price;
//buy back the amount outstanding first
//balance += size * price;
balance += size * (previousPrice - price);
currentShares = 0.0;
//Add the trade
trades.Add(new Trade(date, (int)Trade.Signals.buy, (int)Trade.Positions.coverShort, size, price, balance));
}
endingBalance = balance;
}
public string EndingBalanceString
{
get
{
return String.Format("{0:c}", endingBalance);
}
}
public string PL
{
get
{
pl = endingBalance - startingBalance;
return String.Format("{0:c}", pl);
}
}
public void add(DateTime date, int signal, double price)
{
double size;
int positionType;
if (signal == (int)Trade.Signals.buy)
{
if (currentShares < 0.0) //means they shorted
{
size = ((Trade)trades[trades.Count - 1]).Size;
double previousPrice = ((Trade)trades[trades.Count - 1]).Price;
//buy back the amount outstanding first
//balance += size * price;
balance += size * (previousPrice - price);
currentShares = 0.0;
//Add the trade
trades.Add(new Trade(date, signal, (int)Trade.Positions.coverShort, size, price, balance));
//Buy more with the current balance
size = Math.Floor(balance / price);
balance -= size * price;
currentShares = size;
positionType = (int)Trade.Positions.buyLong;
}
else //buying shares
{
size = Math.Floor(balance / price);
balance -= size * price;
currentShares = size;
positionType = (int)Trade.Positions.buyLong;
}
}
else //Sell
{
//Last value
if (currentShares > 0.0)
{
size = ((Trade)trades[trades.Count - 1]).Size;
balance += size * price;
currentShares = 0.0;
positionType = (int)Trade.Positions.sellLong;
trades.Add(new Trade(date, signal, positionType, size, price, balance));
//Now Short
size = Math.Floor(balance / price);
currentShares = size * -1.0;
positionType = (int)Trade.Positions.sellShort;
}
else //For when the first trade is a sell (aka short)
{
size = Math.Floor(balance / price);
currentShares = size * -1.0;
positionType = (int)Trade.Positions.sellShort;
}
}
trades.Add(new Trade(date, signal, positionType, size, price, balance));
}
public Trade getReportLine(int index)
{
Trade retval = null;
if (index >= 0 && index < trades.Count)
{
retval = (Trade)trades[index];
}
return retval;
}
public int getSize()
{
return trades.Count;
}
}
}