using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.Linq; using MarketData.Utils; using MarketData.Numerical; using MarketData.DataAccess; namespace MarketData.MarketDataModel { public class PriceComparerDesc : IComparer { public int Compare(Price p1, Price p2) { if (p1.Date < p2.Date) return -1; else if (p1.Date > p2.Date) return 1; return 0; } } // Throughout the application it is assumed that these collections, when populated, be be in descending date order. public class Prices : List { public Prices() { } public Prices(Price[] prices) { foreach (Price price in prices) Add(price); } public Prices(List prices) { foreach(Price price in prices)Add(price); } public Prices(String strCSV,String symbol) { String[] csvLines = strCSV.Split('\n'); Clear(); for (int index = 1; index < csvLines.Length; index++) { if (csvLines[index].Length < 1) continue; String[] lineItems = csvLines[index].Split(','); Price price = new Price(); String[] dateParts = lineItems[0].Split('-'); try { price.Date = new DateTime(int.Parse(dateParts[0]), int.Parse(dateParts[1]), int.Parse(dateParts[2])); } catch (Exception /*exception*/) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("'{0}' does not contain a date", lineItems[0])); continue; } price.Symbol = symbol; price.Open = Double.Parse(lineItems[1]); price.High = Double.Parse(lineItems[2]); price.Low = Double.Parse(lineItems[3]); price.Close = Double.Parse(lineItems[4]); price.Volume = Int64.Parse(lineItems[5]); if(lineItems.Length>6)price.AdjClose=Double.Parse(lineItems[6]); Add(price); } } // Assumes that the prices are stored lowest date first public double MaxDrawdown() { return Numerics.MaxDrawdown(GetPrices()); } public double MaxUpside() { return Numerics.MaxUpside(GetPrices()); } public PricesByDate GetPricesByDate() { PricesByDate pricesByDate = new PricesByDate(); for (int index = 0; index < Count; index++) pricesByDate.Add(this[index].Date, this[index]); return pricesByDate; } public DateTime MaxDate() { return this.Max(x=>x.Date); } public DateTime MinDate() { return this.Min(x=>x.Date); } public Prices Top(int count) { Prices prices = new Prices(); for (int index = 0; index < count && index=0 && prices.Count Count) return null; float[] pricesArray=new float[count]; for (int index = startIndex,arrayIndex=0; index < startIndex + count; index++,arrayIndex++) { pricesArray[arrayIndex] = (float)this[index].Close; } return pricesArray; } public float[] GetPricesHigh(int startIndex, int count) { if (startIndex + count > Count) return null; float[] pricesArray = new float[count]; for (int index = startIndex, arrayIndex = 0; index < startIndex + count; index++, arrayIndex++) { pricesArray[arrayIndex] = (float)this[index].High; } return pricesArray; } public float[] GetPricesLow(int startIndex, int count) { if (startIndex + count > Count) return null; float[] pricesArray = new float[count]; for (int index = startIndex, arrayIndex = 0; index < startIndex + count; index++, arrayIndex++) { pricesArray[arrayIndex] = (float)this[index].Low; } return pricesArray; } public float[] GetReturns() { if(Count==0||1==Count)return null; float[] returns = new float[Count - 1]; for (int index = 0; index < Count - 1; index++) { Price currentPrice = this[index]; Price prevPrice = this[index + 1]; if (0.00 == prevPrice.Close) returns[index] = 0.00F; else returns[index] = (float)((currentPrice.Close - prevPrice.Close) / Math.Abs(prevPrice.Close)); } return returns; } public double GetReturn1D() { if(Count<2)return double.NaN; Prices pricesForReturn1D=new Prices(this.Take(2).ToList()); return pricesForReturn1D.GetCumulativeReturn(); } public float[] GetReturns(int dayCount) { if(Count-dayCount<=0)return new float[0]; float[] returns = new float[Count - dayCount]; for (int index = 0; index < Count - dayCount; index++) { Price currentPrice = this[index]; Price prevPrice = this[index + dayCount]; if (0.00 == prevPrice.Close) returns[index] = 0.00F; else returns[index] = (float)((currentPrice.Close - prevPrice.Close) / Math.Abs(prevPrice.Close)); } return returns; } public double GetCumulativeReturn() { float[] returns=GetReturns(); if(null==returns)return double.NaN; double itemReturn=0.00; for(int index=0;index symbolPricesByDate = new Dictionary(); List historicalDates = new List(); while (historicalDates.Count < (months + 5)) // pad the months by 5 { historicalDates.Add(startDate); startDate = dateGenerator.GetPrevMonthStart(startDate); } DateTime requestStartDate = dateGenerator.DaysAddActual(asof, 5); // advance 5 days to provide an error margin for holidays Prices symbolPrices = PricingDA.GetPrices(symbol, requestStartDate, historicalDates[historicalDates.Count - 1]); foreach (Price price in symbolPrices) symbolPricesByDate.Add(price.Date, price); startDate = dateGenerator.GetCurrMonthStart(asof); if(startDate>asof)startDate = dateGenerator.GetPrevMonthStart(asof); // if start date winds up > asof on account of a weekend or holiday then fall back a further month while (prices.Count < (months + 1)) { Price price = GetPrice(symbol, startDate, symbolPricesByDate); if(null == price)return null; prices.Add(price); startDate = dateGenerator.GetPrevMonthStart(startDate); if (startDate < minPricingDate) break; } return prices; } private static Price GetPrice(String symbol,DateTime requestedDate, Dictionary symbolPricesByDate) { int maxAdvanceDays = 5; Price symbolPrice = null; for (int advanceDays = 0; advanceDays < maxAdvanceDays; advanceDays++) { if (!symbolPricesByDate.ContainsKey(requestedDate)) { requestedDate = requestedDate.AddDays(1); continue; } symbolPrice = symbolPricesByDate[requestedDate]; } return symbolPrice; } } // *************************************************************************************************************************************************************************** public class Price { public enum PriceSource{Other=0,BigCharts=1,Yahoo=2,Fidelity=3,Google=4,BarChart=5,Robinhood=6}; // !!IMPORTANT only add to this list. private String symbol; private DateTime date; private double open; private double high; private double low; private double close; private long volume; private double adjClose; private double prevClose; // !!IMPORTANT we don't store this nor do we consider this when evaluating a valid price. private PriceSource source; public Price() { } public Price(Price price) { this.Symbol=price.Symbol; this.Date=price.Date; this.Open=price.Open; this.High=price.High; this.Low=price.Low; this.Close=price.Close; this.Volume=price.Volume; this.AdjClose=price.AdjClose; this.PrevClose=price.PrevClose; this.Source=price.Source; } public Price Clone() { Price clonePrice=new Price(); clonePrice.Symbol=Symbol; clonePrice.Date=Date; clonePrice.Open=Open; clonePrice.High=High; clonePrice.Low=Low; clonePrice.Close=Close; clonePrice.Volume=Volume; clonePrice.AdjClose=AdjClose; clonePrice.PrevClose=PrevClose; clonePrice.Source=Source; return clonePrice; } public PriceSource Source { get{return source;} set{source=value;} } public String SourceAsString() { switch(Source) { case PriceSource.Other : return "Other"; case PriceSource.BigCharts : return "BigCharts"; case PriceSource.Yahoo : return "Yahoo"; case PriceSource.Fidelity : return "Fidelity"; case PriceSource.Google : return "Google"; case PriceSource.BarChart : return "BarChart"; case PriceSource.Robinhood : return "Robinhood"; default: return Constants.CONST_QUESTION; } } public String Symbol { get { return symbol; } set { symbol = value; } } public DateTime Date { get { return date; } set { date = value; } } 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 long Volume { get { return volume; } set { volume = value; } } public double AdjClose { get { return adjClose; } set { adjClose = value; } } public double PrevClose { get { return prevClose; } set { prevClose = value; } } public bool IsValid { get { if(null==symbol)return false; if(Utility.IsEpoch(date))return false; if(double.IsNaN(open))return false; if(double.IsNaN(high))return false; if(double.IsNaN(low))return false; if(double.IsNaN(close))return false; if(double.IsNaN(adjClose))return false; return true; } } public static String Header { get { return "Symbol,Date,Open,High,Low,Close,Volume,Adj Close,Source"; } // ,M12,M26,MACD,Signal,Histogram } public override String ToString() { StringBuilder sb = new StringBuilder(); sb.Append(symbol).Append(","); sb.Append(Utility.DateTimeToStringMMSDDSYYYY(Date)).Append(","); sb.Append(String.Format("{0:0.00}", Open)).Append(","); sb.Append(String.Format("{0:0.00}", High)).Append(","); sb.Append(String.Format("{0:0.00}", Low)).Append(","); sb.Append(String.Format("{0:0.00}", Close)).Append(","); sb.Append(Volume).Append(","); sb.Append(String.Format("{0:0.00}", AdjClose)).Append(","); sb.Append(SourceAsString()); return sb.ToString(); } } }