Initial Commit

This commit is contained in:
2025-03-25 21:42:32 -04:00
parent c266eecfeb
commit 30c33d3cfd
247 changed files with 60107 additions and 0 deletions

View File

@@ -0,0 +1,173 @@
using MarketData.DataAccess;
using MarketData.MarketDataModel;
using MarketData.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MarketData.Generator
{
// *******************************************************************************************************************************************************************
// ************************************************************ A C Q U I R E R S M U L T I P L E U N I T T E S T ************************************************
// *******************************************************************************************************************************************************************
public class AcquirersMultipleCollection : List<AcquirersMultipleItem>
{
public AcquirersMultipleCollection()
{
}
public AcquirersMultipleCollection(AcquirersMultipleCollection collection)
{
foreach(AcquirersMultipleItem acquirersMultipleItem in collection)Add(acquirersMultipleItem);
}
}
public class AcquirersMultipleItem
{
public AcquirersMultipleItem()
{
}
public String Symbol{get;set;}
public double AcquirersMultiple{get;set;}
public double MarketCapitalization{get;set;}
public double EnterpriseValue{get;set;}
public double OperatingEarnings{get;set;}
public double DepreciationAndAmortization{get;set;}
public double TotalRevenue{get;set;}
public double CostOfRevenue{get;set;}
public double SGA{get;set;}
public double Rank{get;set;}
public static String Heading
{
get{return "Symbol,MarketCapitalization,AcquirersMultiple,EnterpriseValue,OperatingEarnings,DepreciationAndAmortization,TotalRevenue,CostOfRevenue,SGA,Rank";}
}
public override string ToString()
{
StringBuilder sb=new StringBuilder();
sb.Append(Symbol).Append(",").Append(Utility.FormatNumber(MarketCapitalization,2,false)).Append(",").Append(Utility.FormatNumber(AcquirersMultiple,2,false)).Append(",").Append(Utility.FormatNumber(EnterpriseValue,2,false)).Append(",");
sb.Append(Utility.FormatNumber(OperatingEarnings,2,false)).Append(",").Append(Utility.FormatNumber(DepreciationAndAmortization,2,false)).Append(",");
sb.Append(Utility.FormatNumber(TotalRevenue,2,false)).Append(",").Append(Utility.FormatNumber(CostOfRevenue,2,false)).Append(",");
sb.Append(Utility.FormatNumber(SGA,2,false)).Append(",").Append(Utility.FormatNumber(Rank,2,false)).Append(",");
return sb.ToString();
}
}
public class AcquirersMultipleItemSorter : IComparer<AcquirersMultipleItem>
{
public int Compare(AcquirersMultipleItem v1,AcquirersMultipleItem v2) // here we want to sort by lowest positive non NaN item
{
if(double.IsNaN(v1.AcquirersMultiple)&&double.IsNaN(v2.AcquirersMultiple))return 0;
else if(double.IsNaN(v1.AcquirersMultiple))return -1;
else if(double.IsNaN(v2.AcquirersMultiple))return 1;
else if(v1.AcquirersMultiple.Equals(v2.AcquirersMultiple))return 0;
else if(v1.AcquirersMultiple<v2.AcquirersMultiple)return 1;
else return -1;
}
}
public class AcquirersMultipeGenerator
{
public static AcquirersMultipleCollection ApplyRanks()
{
List<String> symbols=PricingDA.GetSymbols();
return ApplyRanks(symbols);
}
public static AcquirersMultipleCollection ApplyRanks(String symbol)
{
List<String> symbols=new List<String>();
symbols.Add(symbol);
return ApplyRanks(symbols);
}
public static AcquirersMultipleCollection ApplyRanks(List<String> symbols)
{
// List<String> symbols=PricingDA.GetSymbols();
AcquirersMultipleCollection acquirersMultipleCollection=new AcquirersMultipleCollection();
IncomeStatement incomeStatement=null;
CashflowStatement cashflowStatement=null;
BalanceSheet balanceSheet=null;
// pull in the data for all symbols
foreach(String symbol in symbols)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Working on {0}",symbol));
incomeStatement=IncomeStatementDA.GetIncomeStatement(symbol,IncomeStatement.PeriodType.Annual);
cashflowStatement=CashflowStatementDA.GetCashflowStatement(symbol,CashflowStatement.PeriodType.Annual);
balanceSheet = BalanceSheetDA.GetBalanceSheet(symbol,BalanceSheet.PeriodType.Annual);
Fundamental fundamental = FundamentalDA.GetFundamental(symbol);
AcquirersMultipleItem acquirersMultipleItem=new AcquirersMultipleItem();
acquirersMultipleItem.Symbol=symbol;
if(null!=fundamental&&!double.IsNaN(fundamental.EnterpriseValue)&&null!=cashflowStatement&&!double.IsNaN(cashflowStatement.DepreciationAndAmortization)&&null!=incomeStatement&&!double.IsNaN(incomeStatement.TotalRevenue)&&!double.IsNaN(incomeStatement.CostOfRevenue)&&!double.IsNaN(incomeStatement.SGA))
{
acquirersMultipleItem.MarketCapitalization=fundamental.MarketCap;
acquirersMultipleItem.OperatingEarnings=incomeStatement.TotalRevenue-(cashflowStatement.DepreciationAndAmortization+incomeStatement.CostOfRevenue+incomeStatement.SGA);
acquirersMultipleItem.AcquirersMultiple=fundamental.EnterpriseValue/acquirersMultipleItem.OperatingEarnings;
acquirersMultipleItem.EnterpriseValue=fundamental.EnterpriseValue;
acquirersMultipleItem.DepreciationAndAmortization=cashflowStatement.DepreciationAndAmortization;
acquirersMultipleItem.TotalRevenue=incomeStatement.TotalRevenue;
acquirersMultipleItem.CostOfRevenue=incomeStatement.CostOfRevenue;
acquirersMultipleItem.SGA=incomeStatement.SGA;
}
else
{
if(null==fundamental)MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} missing fundamental.",symbol));
// if(null==balanceSheet)MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} missing balance sheet.",symbol));
if(null==cashflowStatement)MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} missing cashflow statement.",symbol));
if(null==incomeStatement)MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} missing income statement.",symbol));
if(null!=fundamental&&double.IsNaN(fundamental.EnterpriseValue))MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} missing fundamental data point (EnterpriseValue).",symbol));
if(null!=cashflowStatement&&double.IsNaN(cashflowStatement.DepreciationAndAmortization))MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} missing cashflow statement data point (DepreciationAndAmortization).",symbol));
if(null!=incomeStatement&&double.IsNaN(incomeStatement.CostOfRevenue))MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} missing income statement data point (Cost of Revenue).",symbol));
if(null!=incomeStatement&&double.IsNaN(incomeStatement.SGA))MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} missing income statement data point (SGA).",symbol));
acquirersMultipleItem.MarketCapitalization=double.NaN;
acquirersMultipleItem.OperatingEarnings=double.NaN;
acquirersMultipleItem.AcquirersMultiple=double.NaN;
acquirersMultipleItem.EnterpriseValue=double.NaN;
acquirersMultipleItem.DepreciationAndAmortization=double.NaN;
acquirersMultipleItem.TotalRevenue=double.NaN;
acquirersMultipleItem.CostOfRevenue=double.NaN;
acquirersMultipleItem.SGA=double.NaN;
}
acquirersMultipleCollection.Add(acquirersMultipleItem);
}
// Build the rankings
Dictionary<String,AcquirersMultipleItem> acquirersMultipleCollectionBySymbol=new Dictionary<String,AcquirersMultipleItem>();
Dictionary<String,int> acquirersMultipleRankedByAcquirersMultiple=new Dictionary<String,int>();
foreach(AcquirersMultipleItem acquirersMultipleItem in acquirersMultipleCollection)
{
if(!acquirersMultipleCollectionBySymbol.ContainsKey(acquirersMultipleItem.Symbol))acquirersMultipleCollectionBySymbol.Add(acquirersMultipleItem.Symbol,acquirersMultipleItem);
}
acquirersMultipleCollection.Sort(new AcquirersMultipleItemSorter());
for(int index=0;index<acquirersMultipleCollection.Count;index++)
{
String symbol=acquirersMultipleCollection[index].Symbol;
AcquirersMultipleItem acquirersMultipleItem=acquirersMultipleCollectionBySymbol[symbol];
if(double.IsNaN(acquirersMultipleItem.AcquirersMultiple)||acquirersMultipleItem.AcquirersMultiple<=0||acquirersMultipleItem.MarketCapitalization<200000000.00||acquirersMultipleItem.EnterpriseValue<0||acquirersMultipleItem.OperatingEarnings<0)acquirersMultipleItem.Rank=0;
else acquirersMultipleItem.Rank=index;
}
return acquirersMultipleCollection;
}
// *******************************************************************************************************************************************************************
// ********************************************** A C Q U I R E R S M U L T I P L E G E N E R A T O R F O R V A L U A T I O N **********************************
// *******************************************************************************************************************************************************************
public static void ApplyRanks(Valuations valuations)
{
Dictionary<String,Valuation> valuationsBySymbol=new Dictionary<String,Valuation>();
Dictionary<String,int> valuationsRankedByAcquirersMultiple=new Dictionary<String,int>();
foreach(Valuation valuation in valuations)
{
if(!valuationsBySymbol.ContainsKey(valuation.Symbol))valuationsBySymbol.Add(valuation.Symbol,valuation);
}
Valuations valuationsCopyByAcquirersMultiple=new Valuations(valuations);
valuationsCopyByAcquirersMultiple.Sort(new ValuationsByAcquirersMultiple());
for(int index=0;index<valuationsCopyByAcquirersMultiple.Count;index++)
{
String symbol=valuationsCopyByAcquirersMultiple[index].Symbol;
if(!valuationsRankedByAcquirersMultiple.ContainsKey(symbol))valuationsRankedByAcquirersMultiple.Add(symbol,index);
}
for(int index=0;index<valuations.Count;index++)
{
Valuation valuation=valuations[index];
double acquirersMultiple=valuation.AcquirersMultiple;
if(double.IsNaN(acquirersMultiple)||acquirersMultiple<=0||valuation.MarketCap<200000000.00||valuation.EnterpriseValue<0||valuation.OperatingEarnings<0)valuation.AMRank=0;
else valuation.AMRank=valuationsRankedByAcquirersMultiple[valuation.Symbol];
}
}
}
}

View File

@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Text;
using MarketData.Generator.MovingAverage;
using MarketData.MarketDataModel;
// Filename: BollingerBandGenerator.cs
// Author:Sean Kessler
// Date:05/2013
namespace MarketData.Generator
{
/// <summary>GenerateBollingerBands - Bollinger band generator utility class</summary>
public class BollingerBandGenerator
{
private BollingerBandGenerator()
{
}
public static BollingerBands GenerateBollingerBands(Prices prices,int movingAverageDays=20)
{
try
{
double standardDeviations = 2.00;
BollingerBands bollingerBands = new BollingerBands();
Dictionary<DateTime, DMADeviation> deviationsByDate = new Dictionary<DateTime, DMADeviation>();
Dictionary<DateTime, DMAPrice> averagesByDate = new Dictionary<DateTime, DMAPrice>();
if (movingAverageDays < 20) standardDeviations = 1.9;
else if (movingAverageDays >= 20) standardDeviations = 2.1;
if (null == prices || prices.Count < movingAverageDays * 2) return null;
DMAPrices dmaPrices = MovingAverageGenerator.GenerateMovingAverage(prices, movingAverageDays);
DMADeviations dmaDeviations = MovingDeviation.GenerateMovingDeviation(prices, movingAverageDays);
for (int index = 0; index < dmaPrices.Count; index++)
{
DMAPrice dmaPrice = dmaPrices[index];
if (!averagesByDate.ContainsKey(dmaPrice.Date)) averagesByDate.Add(dmaPrice.Date, dmaPrice);
}
for (int index = 0; index < dmaDeviations.Count; index++)
{
DMADeviation dmaDeviation = dmaDeviations[index];
if (!deviationsByDate.ContainsKey(dmaDeviation.Date)) deviationsByDate.Add(dmaDeviation.Date, dmaDeviation);
}
for (int index = 0; index < prices.Count; index++)
{
Price price = prices[index];
if (!deviationsByDate.ContainsKey(price.Date) || !averagesByDate.ContainsKey(price.Date)) continue;
DMADeviation dmaDeviation = deviationsByDate[price.Date];
DMAPrice dmaPrice = averagesByDate[price.Date];
BollingerBandElement bollingerBandElement = new BollingerBandElement();
bollingerBandElement.Symbol = price.Symbol;
bollingerBandElement.Date = price.Date;
bollingerBandElement.Open = price.Open;
bollingerBandElement.High = price.High;
bollingerBandElement.Low = price.Low;
bollingerBandElement.Close = price.Close;
bollingerBandElement.Volume = price.Volume;
bollingerBandElement.SMAN = dmaPrice.AVGPrice;
bollingerBandElement.StDevN = dmaDeviation.StDevPrice;
bollingerBandElement.K = bollingerBandElement.SMAN + (standardDeviations * bollingerBandElement.StDevN);
bollingerBandElement.L = bollingerBandElement.SMAN - (standardDeviations * bollingerBandElement.StDevN);
bollingerBandElement.KL1 = bollingerBandElement.SMAN + ((standardDeviations - 1) * bollingerBandElement.StDevN);
bollingerBandElement.LP1 = bollingerBandElement.SMAN - ((standardDeviations - 1) * bollingerBandElement.StDevN);
bollingerBands.Add(bollingerBandElement);
}
return bollingerBands;
}
catch (Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception);
return null;
}
}
}
}

View File

@@ -0,0 +1,310 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MarketData.DataAccess;
using MarketData.Numerical;
using MarketData.MarketDataModel;
using MarketData.Utils;
namespace MarketData.Generator
{
public class DCFGenerator
{
private DCFGenerator()
{
}
// This just calculates the WACC
public static double CalculateWACC(String symbol,DateTime asOf)
{
try
{
DCFValuation dcfValuation = new DCFValuation();
dcfValuation.Symbol = symbol;
Fundamental fundamental = FundamentalDA.GetFundamentalMaxDate(symbol,asOf);
if (null == fundamental) {return double.NaN; }
if (!CalculateCostOfEquity(dcfValuation,asOf)) return double.NaN;
if (!CalculateCostOfDebt(dcfValuation,fundamental.TotalDebt,asOf)) return double.NaN;
dcfValuation.MarketValueOfDebt = fundamental.TotalDebt;
dcfValuation.MarketValueOfEquity = fundamental.MarketCap;
dcfValuation.OutstandingShares = fundamental.SharesOutstanding;
dcfValuation.TotalCapitalInvested = dcfValuation.MarketValueOfDebt + dcfValuation.MarketValueOfEquity;
if (!CalculateWACC(dcfValuation)) return double.NaN;
return dcfValuation.WACC;
}
catch(Exception)
{
return double.NaN;
}
}
// This calculates the full discounted cashflow
public static DCFValuation GenerateDCFValuation(String symbol)
{
DCFValuation dcfValuation = new DCFValuation();
dcfValuation.Symbol = symbol;
Fundamental fundamental = FundamentalDA.GetFundamental(symbol);
if (null == fundamental) { dcfValuation.Message = "Missing fundamental data."; return dcfValuation; }
if (!CalculateCostOfEquity(dcfValuation)) return dcfValuation;
if (!CalculateCostOfDebt(dcfValuation, fundamental.TotalDebt)) return dcfValuation;
dcfValuation.MarketValueOfDebt = fundamental.TotalDebt;
dcfValuation.MarketValueOfEquity = fundamental.MarketCap;
dcfValuation.OutstandingShares = fundamental.SharesOutstanding;
dcfValuation.TotalCapitalInvested = dcfValuation.MarketValueOfDebt + dcfValuation.MarketValueOfEquity;
if (!CalculateWACC(dcfValuation)) return dcfValuation;
if (!Discount(dcfValuation)) return dcfValuation;
EnhanceValuation(dcfValuation,fundamental); // add ROIC, EPSGrowth, EarningsGrowth
dcfValuation.success = true;
return dcfValuation;
}
public static void EnhanceValuation(DCFValuation dcfValuation,Fundamental fundamental)
{
TimeSeriesCollection bookValuePerShareCollection = HistoricalDA.GetTimeSeries(dcfValuation.Symbol, TimeSeriesElement.ElementType.BVPS);
TimeSeriesCollection roicCollection = HistoricalDA.GetTimeSeries(dcfValuation.Symbol, TimeSeriesElement.ElementType.ROIC);
TimeSeriesCollection revenueCollection = HistoricalDA.GetTimeSeries(dcfValuation.Symbol, TimeSeriesElement.ElementType.Revenue);
TimeSeriesCollection epsCollection = HistoricalDA.GetTimeSeries(dcfValuation.Symbol, TimeSeriesElement.ElementType.EPS);
YieldCurve yieldCurve=YieldCurveDA.GetYieldCurve();
if (null != roicCollection && 0 != roicCollection.Count)
{
dcfValuation.Enhancements.ROICGrowth = Numerics.AverageReturn(roicCollection);
dcfValuation.Enhancements.ROIC = roicCollection[0].Value/100.00;
}
dcfValuation.Enhancements.DebtLoad = fundamental.Revenue * 3 > fundamental.TotalDebt ? true : false;
dcfValuation.Enhancements.EPSGrowth = Numerics.AverageReturn(epsCollection);
dcfValuation.Enhancements.RevenueGrowth = Numerics.AverageReturn(revenueCollection);
dcfValuation.Enhancements.BVPS = fundamental.BookValuePerShare;
if (null == dcfValuation.CurrentPrice || double.NaN.Equals(dcfValuation.CurrentPrice.Close)) dcfValuation.Enhancements.PBVPS = double.NaN;
else dcfValuation.Enhancements.PBVPS = dcfValuation.CurrentPrice.Close / dcfValuation.Enhancements.BVPS;
dcfValuation.Enhancements.EPS = fundamental.EPS;
dcfValuation.Enhancements.PE = fundamental.PE;
dcfValuation.Enhancements.PEG = fundamental.PEG;
dcfValuation.Enhancements.MOS = dcfValuation.StockPriceValuation * .5;
dcfValuation.Enhancements.MOS80 = dcfValuation.StockPriceValuation * .8;
dcfValuation.Enhancements.IntrinsicValue = GrahamGenerator.IntrinsicValue(fundamental.EPS,dcfValuation.Enhancements.EPSGrowth);
if (null == yieldCurve || double.NaN.Equals(yieldCurve[0].Yr20)) dcfValuation.Enhancements.IntrinsicValueRevised = double.NaN;
else dcfValuation.Enhancements.IntrinsicValueRevised = GrahamGenerator.IntrinsicValueRevised(fundamental.EPS,dcfValuation.Enhancements.EPSGrowth,yieldCurve[0].Yr30);
if (null == dcfValuation.CurrentPrice || double.NaN.Equals(dcfValuation.CurrentPrice.Close)) dcfValuation.Enhancements.RGVIntrinsic = double.NaN;
else dcfValuation.Enhancements.RGVIntrinsic = dcfValuation.Enhancements.IntrinsicValue / dcfValuation.CurrentPrice.Close;
if (null == dcfValuation.CurrentPrice || double.NaN.Equals(dcfValuation.CurrentPrice.Close)) dcfValuation.Enhancements.RGVIntrinsicRevised = double.NaN;
else dcfValuation.Enhancements.RGVIntrinsicRevised = dcfValuation.Enhancements.IntrinsicValueRevised / dcfValuation.CurrentPrice.Close;
}
private static bool Discount(DCFValuation dcfValuation,int years=10)
{
List<Element> sourceElements = new List<Element>();
List<Element> destElements = null;
// pull cashflow history from database and apply filters
TimeSeriesCollection freeCashflows = HistoricalDA.GetTimeSeries(dcfValuation.Symbol, TimeSeriesElement.ElementType.FreeCashflow);
if (null == freeCashflows || 0 == freeCashflows.Count) { dcfValuation.Message = "Missing Freecashflows"; return false; }
dcfValuation.FreeCashflowGrowth = Numerics.AverageReturnWithSpline(freeCashflows,dcfValuation.ReturnItems); // if free cashflow growth > 100% perform an average throwing away all returns in the set > 100%
if (dcfValuation.FreeCashflowGrowth.Equals(double.NaN)) { dcfValuation.Message = "Freecashflow growth is undefined"; return false; }
// set up spline parameters. Apply 3% cap to freecashflow growth in the 10th year and spline to that.
sourceElements.Add(new Element(1, dcfValuation.FreeCashflowGrowth));
sourceElements.Add(new Element(years, .03 < dcfValuation.FreeCashflowGrowth ? .03 : dcfValuation.FreeCashflowGrowth));
// set the starting date and the initial freecashflow value, run interpolation
DateTime startingDate = freeCashflows[0].AsOf;
double runningCashflow = freeCashflows[0].Value;
for (int year = 1; year <= years; year++)
{
startingDate = new DateTime(startingDate.Year + 1, startingDate.Month, startingDate.Day);
destElements = new List<Element>();
destElements.Add(new Element(year, 0));
CatmullRom.PerformSpline((Element[])sourceElements.ToArray(), (Element[])destElements.ToArray());
double cashflowGrowth = destElements[0].Row;
if (year < years) runningCashflow = runningCashflow * (1.00 + cashflowGrowth);
else runningCashflow = (runningCashflow * (1.00 + cashflowGrowth)) / (Math.Abs(dcfValuation.WACC - cashflowGrowth));
dcfValuation.Cashflows.Add(new CashflowElement(startingDate, runningCashflow));
}
// Discount cashflows by WACC
for (int index = 0; index < dcfValuation.Cashflows.Count; index++)
{
dcfValuation.Cashflows[index].Cashflow /= Math.Pow(1.00 + dcfValuation.WACC, index + 1);
}
// sum up the discounted values to get the present value;
dcfValuation.PresentValue = 0.00;
for (int index = 0; index < dcfValuation.Cashflows.Count; index++)
{
dcfValuation.PresentValue += dcfValuation.Cashflows[index].Cashflow;
}
// subtract debt
dcfValuation.PresentValue -= dcfValuation.TotalDebt;
// divide by outstanding shares
dcfValuation.StockPriceValuation=dcfValuation.PresentValue/dcfValuation.OutstandingShares;
// Apply validation filters
if (dcfValuation.PresentValue.Equals(double.NaN)) { dcfValuation.Message = "PresentValue is undefined"; return false; }
if (dcfValuation.StockPriceValuation.Equals(double.NaN)) { dcfValuation.Message = "Stock price valuation is undefined"; return false; }
return true;
}
// Calculate the WACC
private static bool CalculateWACC(DCFValuation dcfValuation)
{
dcfValuation.WACC=((dcfValuation.MarketValueOfEquity/dcfValuation.TotalCapitalInvested)*dcfValuation.CostOfEquity)+
((dcfValuation.MarketValueOfDebt/dcfValuation.TotalCapitalInvested)*dcfValuation.CostOfDebt*(1.00-dcfValuation.CorporateTaxRate));
return true;
}
private static bool CalculateCostOfEquity(DCFValuation dcfValuation)
{
String marketSymbol = "SPY";
Prices stockPrices = null;
Prices marketPrices=null;
// calculate 3-year beta of stock vs SP500
stockPrices = PricingDA.GetPrices(dcfValuation.Symbol, 365 * 3);
if (null == stockPrices || 0 == stockPrices.Count)
{
dcfValuation.Message = "Missing prices for symbol.";
return false;
}
dcfValuation.CurrentPrice = stockPrices[0];
marketPrices=PricingDA.GetPrices(marketSymbol,stockPrices.Count);
if (null == stockPrices || 0 == stockPrices.Count || null == marketPrices || 0 == marketPrices.Count)
{
dcfValuation.Message = "Missing prices for market.";
return false;
}
dcfValuation.Beta = BetaGenerator.Beta(dcfValuation.Symbol, PricingDA.GetLatestDate(dcfValuation.Symbol));
// get the risk free rate, 90 day treasury note
dcfValuation.RiskFreeRate = YieldCurveDA.GetRiskFreeRate();
dcfValuation.RiskFreeRate /= 100.00;
// calculate historic market returns
if (dcfValuation.UseMarketReturn)
{
marketPrices = PricingDA.GetPrices(marketSymbol);
float[] marketPricesArray = marketPrices.GetPrices();
dcfValuation.MarketReturn = Numerics.AnnualReturn(ref marketPricesArray);
}
else dcfValuation.MarketReturn = DCFValuation.MARKET_RETURN;
// calculate cost of equity
dcfValuation.CostOfEquity = dcfValuation.RiskFreeRate + dcfValuation.Beta * (dcfValuation.MarketReturn - dcfValuation.RiskFreeRate);
return true;
}
private static bool CalculateCostOfEquity(DCFValuation dcfValuation,DateTime asOf)
{
String marketSymbol = "SPY";
Prices stockPrices = null;
Prices marketPrices=null;
// calculate 3-year beta of stock vs SP500
stockPrices = PricingDA.GetPrices(dcfValuation.Symbol,asOf, 365 * 3);
if (null == stockPrices || 0 == stockPrices.Count)
{
dcfValuation.Message = "Missing prices for symbol.";
return false;
}
dcfValuation.CurrentPrice = stockPrices[0];
marketPrices=PricingDA.GetPrices(marketSymbol,asOf,stockPrices.Count);
if (null == stockPrices || 0 == stockPrices.Count || null == marketPrices || 0 == marketPrices.Count)
{
dcfValuation.Message = "Missing prices for market.";
return false;
}
//float[] stockPricesArray=stockPrices.GetPrices();
//float[] marketPricesArray = marketPrices.GetPrices();
//dcfValuation.Beta = Numerics.Beta(ref stockPricesArray, ref marketPricesArray);
dcfValuation.Beta = BetaGenerator.Beta(dcfValuation.Symbol, PricingDA.GetLatestDate(dcfValuation.Symbol));
// get the risk free rate, 90 day treasury note
dcfValuation.RiskFreeRate = YieldCurveDA.GetRiskFreeRate(asOf);
dcfValuation.RiskFreeRate /= 100.00;
// calculate historic market returns
if (dcfValuation.UseMarketReturn)
{
marketPrices = PricingDA.GetPricesOnOrBefore(marketSymbol,asOf);
float[] marketPricesArray = marketPrices.GetPrices();
dcfValuation.MarketReturn = Numerics.AnnualReturn(ref marketPricesArray);
}
else dcfValuation.MarketReturn = DCFValuation.MARKET_RETURN;
// calculate cost of equity
dcfValuation.CostOfEquity = dcfValuation.RiskFreeRate + dcfValuation.Beta * (dcfValuation.MarketReturn - dcfValuation.RiskFreeRate);
return true;
}
// **YAHOO InterestExpense is totally wrong for some stocks. AER is a good example
// This prioritizes MorningStar InterestExpense number over Yahoo Interest Expense number
private static bool CalculateCostOfDebt(DCFValuation dcfValuation,double totalDebt)
{
TimeSeriesCollection interestExpenseSeries = HistoricalDA.GetTimeSeries(dcfValuation.Symbol, TimeSeriesElement.ElementType.InterestExpense);
if (null == interestExpenseSeries || 0 == interestExpenseSeries.Count)
{
IncomeStatement incomeStatement = IncomeStatementDA.GetIncomeStatement(dcfValuation.Symbol, IncomeStatement.PeriodType.Annual);
if(null!=incomeStatement&&incomeStatement.InterestExpense.Equals(double.NaN))
{
dcfValuation.InterestExpense=0.00;
}
else if(null==incomeStatement)
{
dcfValuation.Message = "Missing IncomeStatement or Interest Expense ";
return false;
}
else dcfValuation.InterestExpense=incomeStatement.InterestExpense;
}
else
{
if(interestExpenseSeries[0].Value.Equals(double.NaN))dcfValuation.InterestExpense=0.00;
else dcfValuation.InterestExpense=interestExpenseSeries[0].Value;
}
TimeSeriesCollection timeSeriesCollection = HistoricalDA.GetTimeSeries(dcfValuation.Symbol, TimeSeriesElement.ElementType.TaxRate);
if (null == timeSeriesCollection || 0 == timeSeriesCollection.Count) timeSeriesCollection.Add(new TimeSeriesElement() {Value=37 });
TimeSeriesElement timeSeriesElement = timeSeriesCollection[0];
dcfValuation.CorporateTaxRate = timeSeriesElement.Value / 100.00;
dcfValuation.TotalDebt = totalDebt;
if(0.00==totalDebt)
{
dcfValuation.InterestRate=0.00;
dcfValuation.CostOfDebt=0.00;
}
else
{
dcfValuation.InterestRate=dcfValuation.InterestExpense/dcfValuation.TotalDebt;
dcfValuation.CostOfDebt=dcfValuation.InterestRate*(1-dcfValuation.CorporateTaxRate);
}
return true;
}
private static bool CalculateCostOfDebt(DCFValuation dcfValuation,double totalDebt,DateTime asOf)
{
TimeSeriesCollection interestExpenseSeries = HistoricalDA.GetTimeSeriesMaxAsOf(dcfValuation.Symbol,asOf, TimeSeriesElement.ElementType.InterestExpense);
if (null == interestExpenseSeries || 0 == interestExpenseSeries.Count)
{
IncomeStatement incomeStatement = IncomeStatementDA.GetIncomeStatementMaxAsOf(dcfValuation.Symbol,asOf, IncomeStatement.PeriodType.Annual);
if(null!=incomeStatement&&incomeStatement.InterestExpense.Equals(double.NaN))
{
dcfValuation.InterestExpense=0.00;
}
else if(null==incomeStatement)
{
dcfValuation.Message = "Missing IncomeStatement or Interest Expense ";
return false;
}
else dcfValuation.InterestExpense=incomeStatement.InterestExpense;
}
else
{
if(interestExpenseSeries[0].Value.Equals(double.NaN))dcfValuation.InterestExpense=0.00;
else dcfValuation.InterestExpense=interestExpenseSeries[0].Value;
}
TimeSeriesCollection timeSeriesCollection = HistoricalDA.GetTimeSeriesMaxAsOf(dcfValuation.Symbol,asOf, TimeSeriesElement.ElementType.TaxRate);
if (null == timeSeriesCollection || 0 == timeSeriesCollection.Count) timeSeriesCollection.Add(new TimeSeriesElement() {Value=37 });
TimeSeriesElement timeSeriesElement = timeSeriesCollection[0];
dcfValuation.CorporateTaxRate = timeSeriesElement.Value / 100.00;
dcfValuation.TotalDebt = totalDebt;
if(0.00==totalDebt)
{
dcfValuation.InterestRate=0.00;
dcfValuation.CostOfDebt=0.00;
}
else
{
dcfValuation.InterestRate=dcfValuation.InterestExpense/dcfValuation.TotalDebt;
dcfValuation.CostOfDebt=dcfValuation.InterestRate*(1-dcfValuation.CorporateTaxRate);
}
return true;
}
}
}

View File

@@ -0,0 +1,114 @@
using MarketData.Cache;
using MarketData.DataAccess;
using MarketData.MarketDataModel;
using MarketData.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MarketData.Generator
{
public class DividendHistoryGenerator
{
private DividendHistoryGenerator()
{
}
public static double GetDividendYield(String symbol)
{
try
{
Price currentPrice=PricingDA.GetPrice(symbol);
return GetDividendYield(symbol,currentPrice);
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0}",exception.ToString()));
return double.NaN;
}
}
public static double GetDividendYield(String symbol,Price currentPrice)
{
try
{
int currentYear=DateTime.Now.Year;
int priorYear=currentYear-1;
DividendHistory dividendHistory=DividendHistoryCache.GetInstance().GetDividendHistory(symbol,new int[]{currentYear, priorYear});
// DividendHistory dividendHistory=DividendHistoryDA.GetDividendHistory(symbol,new int[] { currentYear,priorYear });
if(null==currentPrice||double.IsNaN(currentPrice.Close)||!currentPrice.Date.Year.Equals(currentYear)) return double.NaN;
DividendHistory lastYearsDividendPayments=new DividendHistory((from DividendHistoryItem item in dividendHistory where item.DivExDate.Year==priorYear&&null!=item.CashAmount select item).ToList());
DividendHistory currentYearsDividendPayments=new DividendHistory((from DividendHistoryItem item in dividendHistory where item.DivExDate.Year==currentYear&&null!=item.CashAmount select item).ToList());
if(currentYearsDividendPayments.HasConsecutivePayments(3)) return (currentYearsDividendPayments.Average(x => x.CashAmount.Value)*12)/currentPrice.Close;
if(0==lastYearsDividendPayments.Count&&0==currentYearsDividendPayments.Count) return double.NaN;
double lastYearsAverageDividendPayment=lastYearsDividendPayments.Count()>1?lastYearsDividendPayments.Average(x => x.CashAmount.Value):double.NaN;
// calculate last years min and max dividend payments in order to determine if there were special payments
double lastYearMinDividendPayment=lastYearsDividendPayments.Count>0?lastYearsDividendPayments.Min(x => x.CashAmount.Value):0.00;
DividendHistory lastYearsSpecialPayments=lastYearsDividendPayments.Count>0?new DividendHistory((from DividendHistoryItem item in lastYearsDividendPayments where item.CashAmount.Value>3*lastYearMinDividendPayment select item).ToList()):new DividendHistory();
bool lastYearHasSpecialItems=lastYearsSpecialPayments.Count()>0?true:false;
if(0==currentYearsDividendPayments.Count) // if no dividends in current year then go back to last year
{
if(lastYearHasSpecialItems) lastYearsDividendPayments=new DividendHistory(lastYearsDividendPayments.Where(x => !lastYearsSpecialPayments.Any(z => z.DivExDate==x.DivExDate)).ToList());
else if(1==lastYearsDividendPayments.Count) return lastYearsDividendPayments[0].CashAmount.Value/currentPrice.Close;
return lastYearsDividendPayments.Sum(x => x.CashAmount.Value)/currentPrice.Close;
}
double currentYearMaxDividendPayment=currentYearsDividendPayments.Max(x => x.CashAmount.Value);
double currentYearMinDividendPayment=currentYearsDividendPayments.Min(x => x.CashAmount.Value);
DividendHistory currentYearsSpecialPayments=new DividendHistory((from DividendHistoryItem item in currentYearsDividendPayments where item.CashAmount.Value>3*currentYearMinDividendPayment select item).ToList());
bool currentYearhasSpecialItems=currentYearsSpecialPayments.Count()>0?true:false;
double currentYearsAverageDividendPayment=currentYearsDividendPayments.Average(x => x.CashAmount.Value);
if(1==lastYearsDividendPayments.Count&&1==currentYearsDividendPayments.Count) return currentYearsAverageDividendPayment/currentPrice.Close;
if(0==lastYearsDividendPayments.Count) // there was no dividend payment last year. check if this years is a special dividend (>25%)
{
double dividendPayment=currentYearsDividendPayments.Average(x => x.CashAmount.Value);
double dividendYield=dividendPayment/currentPrice.Close;
if(dividendYield>=.25) return dividendYield;
}
if(4==lastYearsDividendPayments.Count)
{
if(currentYearhasSpecialItems) // exclude special payments from consideration
{
currentYearsDividendPayments=new DividendHistory(currentYearsDividendPayments.Where(x => !currentYearsSpecialPayments.Any(z => z.DivExDate==x.DivExDate)).ToList());
double dividendYield=currentYearsDividendPayments.Average(x => x.CashAmount.Value);
dividendYield*=4.00;
dividendYield/=currentPrice.Close;
return dividendYield;
}
if(1==currentYearsDividendPayments.Count&&lastYearHasSpecialItems) // if only one payment this year and last year has a special payment then use last year minus special payment
{
lastYearsDividendPayments=new DividendHistory(lastYearsDividendPayments.Where(x => !lastYearsSpecialPayments.Any(z => z.DivExDate==x.DivExDate)).ToList());
double dividendYield=lastYearsDividendPayments.Average(x => x.CashAmount.Value);
dividendYield*=4.00;
dividendYield/=currentPrice.Close;
return dividendYield;
}
return (currentYearsAverageDividendPayment*4.00)/currentPrice.Close;
}
if(3==currentYearsDividendPayments.Count)
{
if(currentYearhasSpecialItems) // exclude special payments from consideration
{
currentYearsDividendPayments=new DividendHistory(currentYearsDividendPayments.Where(x => !currentYearsSpecialPayments.Any(z => z.DivExDate==x.DivExDate)).ToList());
double dividendYield=currentYearsDividendPayments.Average(x => x.CashAmount.Value);
dividendYield*=4.00;
dividendYield/=currentPrice.Close;
return dividendYield;
}
else return (currentYearsAverageDividendPayment*4.00)/currentPrice.Close;
}
if(2==lastYearsDividendPayments.Count) return Math.Min(lastYearsDividendPayments.Sum(x => x.CashAmount.Value),currentYearsDividendPayments.Sum(x => x.CashAmount.Value))/currentPrice.Close;
return (currentYearsDividendPayments.Average(x => x.CashAmount.Value)*4.00)/currentPrice.Close;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0}",exception.ToString()));
return double.NaN;
}
}
}
}

View File

@@ -0,0 +1,91 @@
using System;
using System.Linq;
using System.Collections.Generic;
using MarketData;
using MarketData.MarketDataModel;
using MarketData.MarketDataModel.GainLoss;
using MarketData.Utils;
using MarketData.DataAccess;
using MarketData.Cache;
namespace MarketData.Generator.GainLoss
{
// ***************************************************************** G A I N L O S S G E N E R A T O R **********************************************************
public class ActiveGainLossGenerator : IActiveGainLossGenerator
{
public ActiveGainLossGenerator()
{
}
//public void RefreshPriceCache()
//{
// LocalPriceCache.GetInstance().Refresh();
//}
// *****************************************************************************************************************************************************************
// ************************************************ G E N E R A T E A C T I V E G A I N L O S S / G A I N L O S S P E R C E N T *****************************
// *****************************************************************************************************************************************************************
public GainLossCollection GenerateGainLoss(PortfolioTrades portfolioTrades,DateTime? maxDateRef=null)
{
if (null == portfolioTrades || 0 == portfolioTrades.Count) return null;
LocalPriceCache.GetInstance().Add(portfolioTrades);
DateTime minTradeDate = portfolioTrades.GetMinTradeDate();
DateTime maxDate = PricingDA.GetLatestDate();
if(null!=maxDateRef)maxDate=maxDateRef.Value;
Dictionary<DateTime,GainLossItem> gainLoss = new Dictionary<DateTime, GainLossItem>();
DateGenerator dateGenerator = new DateGenerator();
List<DateTime> holdingDates = dateGenerator.GenerateHistoricalDates(maxDate, minTradeDate);
for (int index = holdingDates.Count - 1; index >= 0; index--)
{
DateTime holdingDate = holdingDates[index];
double? gainLossHoldings = null;
double totalExposure = 0.00;
double totalCostBasis=0.00;
double totalMarketValue=0.00;
PortfolioTrades openTrades = portfolioTrades.GetOpenTradesOn(holdingDate);
if (null == openTrades || 0 == openTrades.Count)
{
gainLoss.Add(holdingDate, new GainLossItem(holdingDate, 0,0,false));
continue;
}
if(!LocalPriceCache.GetInstance().ContainsPrice(openTrades.Symbols,holdingDate))
{
if(holdingDate.Date.Equals(maxDate))
{
LocalPriceCache.GetInstance().Add(openTrades.Symbols,holdingDate);
}else continue;
}
foreach (PortfolioTrade portfolioTrade in openTrades)
{
double? gainLossItem = null;
double? exposure = null;
double? costBasis=null;
double? marketValue=null;
costBasis=GainLossHelper.GetCostBasis(holdingDate,portfolioTrade);
gainLossItem=GainLossHelper.GetGainLoss(holdingDate, portfolioTrade);
marketValue=GainLossHelper.GetMarketValue(holdingDate,portfolioTrade);
if (null == gainLossItem) continue;
if (null == gainLossHoldings) gainLossHoldings = gainLossItem;
else gainLossHoldings += gainLossItem;
exposure = GainLossHelper.GetExposure(holdingDate,portfolioTrade);
if (null != exposure) totalExposure += exposure.Value;
if(null!=costBasis)totalCostBasis+=costBasis.Value;
if(null!=marketValue)totalMarketValue+=marketValue.Value;
}
GainLossItem gainLossElement = null;
double totalGainLossPercent=0;
if(0!=totalCostBasis)totalGainLossPercent=((totalMarketValue-totalCostBasis)/totalCostBasis)*100.00;
if(null==gainLossHoldings)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("ActiveGainLossGenerator:GenerateGainLoss 'GainLossHoldings' is null for on {0}",Utility.DateTimeToStringMMHDDHYYYY(holdingDate)));
continue;
}
if (null == gainLossHoldings)gainLossElement = new GainLossItem(holdingDate,gainLossHoldings.Value,totalGainLossPercent,totalExposure,false);
else gainLossElement = new GainLossItem(holdingDate, gainLossHoldings.Value,totalGainLossPercent,totalExposure,false);
gainLoss.Add(holdingDate, gainLossElement);
}
GainLossCollection gainLossList = new GainLossCollection(gainLoss.Values);
gainLossList.Sort();
return gainLossList;
}
}
}

View File

@@ -0,0 +1,127 @@
using System;
using System.Linq;
using System.Collections.Generic;
using MarketData;
using MarketData.MarketDataModel;
using MarketData.MarketDataModel.GainLoss;
using MarketData.Utils;
using MarketData.DataAccess;
using MarketData.Cache;
namespace MarketData.Generator.GainLoss
{
// ***************************************************************** G A I N L O S S G E N E R A T O R **********************************************************
public class GainLossGenerator : ITotalGainLossGenerator
{
public GainLossGenerator()
{
}
// *****************************************************************************************************************************************************************
// ************************************************ G E N E R A T E T O T A L G A I N L O S S / T O T A L G A I N L O S S P E R C E N T *******************
// *****************************************************************************************************************************************************************
public TotalGainLossCollection GenerateTotalGainLoss(PortfolioTrades portfolioTrades,DateTime? maxDateRef=null)
{
if (null == portfolioTrades || 0 == portfolioTrades.Count) return null;
LocalPriceCache.GetInstance().Add(portfolioTrades);
DateTime minTradeDate = portfolioTrades.GetMinTradeDate();
DateTime maxDate = PricingDA.GetLatestDate();
if(null!=maxDateRef)maxDate=maxDateRef.Value;
Dictionary<DateTime,TotalGainLossItem> gainLossCollection = new Dictionary<DateTime, TotalGainLossItem>();
DateGenerator dateGenerator = new DateGenerator();
List<DateTime> holdingDates = dateGenerator.GenerateHistoricalDates(maxDate, minTradeDate);
for (int index = holdingDates.Count - 1; index >= 0; index--)
{
DateTime holdingDate = holdingDates[index];
double totalGainLoss = 0.00;;
double totalExposure = 0.00;
double totalCostBasis=0.00;
double totalMarketValue=0.00;
PortfolioTrades tradesOnOrBefore = portfolioTrades.GetTradesOnOrBefore(holdingDate);
if (null == tradesOnOrBefore || 0 == tradesOnOrBefore.Count)
{
gainLossCollection.Add(holdingDate, new TotalGainLossItem(holdingDate, 0,0,0,0));
continue;
}
foreach (PortfolioTrade portfolioTrade in tradesOnOrBefore)
{
double? gainLoss = null;
double? exposure = null;
double? costBasis=null;
double? marketValue=null;
gainLoss=GainLossHelper.GetTotalGainLoss(holdingDate,portfolioTrade);
costBasis=GainLossHelper.GetTotalCostBasis(holdingDate,portfolioTrade);
marketValue=GainLossHelper.GetTotalMarketValue(holdingDate,portfolioTrade);
exposure=GainLossHelper.GetTotalExposure(holdingDate,portfolioTrade);
if (null == gainLoss || null==costBasis || null==marketValue) continue;
if(null!=gainLoss) totalGainLoss += gainLoss.Value;
if (null != exposure) totalExposure += exposure.Value;
if(null!=costBasis)totalCostBasis+=costBasis.Value;
if(null!=marketValue)totalMarketValue+=marketValue.Value;
}
TotalGainLossItem gainLossElement = null;
double totalGainLossPercent=0;
if(0!=totalCostBasis)totalGainLossPercent=((totalMarketValue-totalCostBasis)/totalCostBasis)*100.00;
gainLossElement=new TotalGainLossItem(holdingDate,totalGainLoss,totalGainLossPercent,totalExposure,totalMarketValue);
gainLossCollection.Add(holdingDate, gainLossElement);
}
TotalGainLossCollection gainLossList = new TotalGainLossCollection(gainLossCollection.Values);
gainLossList.Sort();
return gainLossList;
}
// *****************************************************************************************************************************************************************
// ************************************************ G E N E R A T E T O T A L G A I N L O S S W I T H D I V I D E N D S / T O T A L G A I N L O S S P E R C E N T *******************
// *****************************************************************************************************************************************************************
public TotalGainLossCollection GenerateTotalGainLossWithDividends(PortfolioTrades portfolioTrades,DividendPayments dividendPayments,DateTime? maxDateRef=null)
{
if (null == portfolioTrades || 0 == portfolioTrades.Count) return null;
LocalPriceCache.GetInstance().Add(portfolioTrades);
DateTime minTradeDate = portfolioTrades.GetMinTradeDate();
DateTime maxDate = PricingDA.GetLatestDate();
if(null!=maxDateRef)maxDate=maxDateRef.Value;
Dictionary<DateTime,TotalGainLossItem> gainLossCollection = new Dictionary<DateTime, TotalGainLossItem>();
DateGenerator dateGenerator = new DateGenerator();
List<DateTime> holdingDates = dateGenerator.GenerateHistoricalDates(maxDate, minTradeDate);
for (int index = holdingDates.Count - 1; index >= 0; index--)
{
DateTime holdingDate = holdingDates[index];
double totalGainLoss = 0.00;;
double totalExposure = 0.00;
double totalCostBasis=0.00;
double totalMarketValue=0.00;
PortfolioTrades tradesOnOrBefore = portfolioTrades.GetTradesOnOrBefore(holdingDate);
if (null == tradesOnOrBefore || 0 == tradesOnOrBefore.Count)
{
gainLossCollection.Add(holdingDate, new TotalGainLossItem(holdingDate, 0,0,0,0));
continue;
}
foreach (PortfolioTrade portfolioTrade in tradesOnOrBefore)
{
double? gainLoss = null;
double? exposure = null;
double? costBasis=null;
double? marketValue=null;
gainLoss=GainLossHelper.GetTotalGainLoss(holdingDate,portfolioTrade);
costBasis=GainLossHelper.GetTotalCostBasis(holdingDate,portfolioTrade);
marketValue=GainLossHelper.GetTotalMarketValue(holdingDate,portfolioTrade);
exposure=GainLossHelper.GetTotalExposure(holdingDate,portfolioTrade);
if (null == gainLoss || null==costBasis || null==marketValue) continue;
if(null!=gainLoss) totalGainLoss += gainLoss.Value;
if (null != exposure) totalExposure += exposure.Value;
if(null!=costBasis)totalCostBasis+=costBasis.Value;
if(null!=marketValue)totalMarketValue+=marketValue.Value;
}
TotalGainLossItem gainLossElement = null;
double totalGainLossPercent=0;
double dividendPaymentsToDate=dividendPayments.GetDividendPaymentsToDate(holdingDate);
totalMarketValue+=dividendPaymentsToDate;
totalGainLoss+=dividendPaymentsToDate;
if(0!=totalCostBasis)totalGainLossPercent=((totalMarketValue-totalCostBasis)/totalCostBasis)*100.00;
gainLossElement=new TotalGainLossItem(holdingDate,totalGainLoss,totalGainLossPercent,totalExposure,totalMarketValue,dividendPaymentsToDate);
gainLossCollection.Add(holdingDate, gainLossElement);
}
TotalGainLossCollection gainLossList = new TotalGainLossCollection(gainLossCollection.Values);
gainLossList.Sort();
return gainLossList;
}
}
}

View File

@@ -0,0 +1,193 @@
using System;
using System.Linq;
using System.Collections.Generic;
using MarketData;
using MarketData.MarketDataModel;
using MarketData.MarketDataModel.GainLoss;
using MarketData.Utils;
using MarketData.DataAccess;
using MarketData.Cache;
namespace MarketData.Generator.GainLoss
{
public class GainLossGeneratorCum : ITotalGainLossGenerator
{
private DateGenerator dateGenerator=new DateGenerator();
public TotalGainLossCollection GenerateTotalGainLoss(PortfolioTrades portfolioTrades,DateTime? maxDateRef=null)
{
DateGenerator dateGenerator=new DateGenerator();
ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries();
List<TotalGainLossItem> gainLossList=new List<TotalGainLossItem>();
LocalPriceCache.GetInstance().Add(portfolioTrades);
try
{
if(!ValidatePortfolioTrades(portfolioTrades))return null;
DateTime minDate=portfolioTrades.GetMinTradeDate();
DateTime maxDate=PricingDA.GetLatestDate();
if(null!=maxDateRef) maxDate=maxDateRef.Value;
double prevGainLoss=double.NaN;
List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(minDate,maxDate);
foreach(DateTime currentDate in historicalDates)
{
PortfolioTrades openPositions=portfolioTrades.GetOpenTradesOn(currentDate);
PortfolioTrades closedPositions=portfolioTrades.GetClosedTradesOn(currentDate);
if(0==openPositions.Count&&0==closedPositions.Count) continue;
double gainLoss=0.00;
double gainLossClosedPositions=0.00;
double exposure=0.00;
double marketValue=0.00;
if(!dateGenerator.IsMarketOpen(currentDate)) continue;
ModelPerformanceItem performanceItem=new ModelPerformanceItem();
foreach(PortfolioTrade openPosition in openPositions)
{
exposure+=openPosition.Shares*openPosition.Price;
Price price=LocalPriceCache.GetInstance().GetPrice(openPosition.Symbol,currentDate);
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString()));
continue;
}
gainLoss+=((price.Close*openPosition.Shares)-(openPosition.Price*openPosition.Shares));
marketValue+=(price.Close*openPosition.Shares);
}
foreach(PortfolioTrade closedPosition in closedPositions)
{
double gainLossPosition=(closedPosition.SellPrice*closedPosition.Shares)-(closedPosition.Price*closedPosition.Shares);
gainLossClosedPositions+=gainLossPosition;
}
performanceItem.Date=currentDate;
performanceItem.Exposure=exposure;
performanceItem.MarketValue=marketValue;
performanceItem.GainLossDOD=double.IsNaN(prevGainLoss)?gainLoss:(gainLoss-prevGainLoss)+gainLossClosedPositions;
performanceItem.GainLoss=gainLoss+gainLossClosedPositions;
performanceItem.ClosedPositions=closedPositions.Count>0?true:false;
performanceSeries.Add(performanceItem);
prevGainLoss=gainLoss;
}
performanceSeries.CalculatePerformance();
double totalGainLoss=double.NaN;
foreach(ModelPerformanceItem performanceItem in performanceSeries)
{
if(double.IsNaN(totalGainLoss))totalGainLoss=performanceItem.GainLossDOD;
else totalGainLoss+=performanceItem.GainLossDOD;
TotalGainLossItem totalGainLossItem=new TotalGainLossItem(
performanceItem.Date,
totalGainLoss,
performanceItem.CumProdMinusOne*100.00,
performanceItem.Exposure,
performanceItem.MarketValue);
gainLossList.Add(totalGainLossItem);
}
TotalGainLossCollection totalGainLossCollection= new TotalGainLossCollection(gainLossList);
totalGainLossCollection.Sort();
return totalGainLossCollection;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception: {0}",exception.ToString()));
return new TotalGainLossCollection(new List<TotalGainLossItem>());
}
}
public TotalGainLossCollection GenerateTotalGainLossWithDividends(PortfolioTrades portfolioTrades,DividendPayments dividendPayments,DateTime? maxDateRef=null)
{
DateGenerator dateGenerator=new DateGenerator();
ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries();
List<TotalGainLossItem> gainLossList=new List<TotalGainLossItem>();
LocalPriceCache.GetInstance().Add(portfolioTrades);
try
{
if(!ValidatePortfolioTrades(portfolioTrades)) return null;
DateTime minDate=portfolioTrades.Min(x => x.TradeDate);
DateTime maxDate=PricingDA.GetLatestDate();
double prevGainLoss=double.NaN;
// LocalPriceCache.GetInstance().RemoveDate(maxDate);
List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(minDate,maxDate);
foreach(DateTime currentDate in historicalDates)
{
PortfolioTrades openPositions=portfolioTrades.GetOpenTradesOn(currentDate);
PortfolioTrades closedPositions=portfolioTrades.GetClosedTradesOn(currentDate);
if(0==openPositions.Count&&0==closedPositions.Count) continue;
double gainLoss=0.00;
double gainLossClosedPositions=0.00;
double exposure=0.00;
double marketValue=0.00;
if(!dateGenerator.IsMarketOpen(currentDate)) continue;
ModelPerformanceItem performanceItem=new ModelPerformanceItem();
foreach(PortfolioTrade openPosition in openPositions)
{
exposure+=openPosition.Shares*openPosition.Price;
//if(!LocalPriceCache.GetInstance().ContainsPrice(openPosition.Symbol,currentDate))
//{
// Prices prices=PricingDA.GetPricesForward(openPosition.Symbol,currentDate,90);
// LocalPriceCache.GetInstance().Add(prices);
//}
Price price=LocalPriceCache.GetInstance().GetPrice(openPosition.Symbol,currentDate);
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString()));
}
gainLoss+=((price.Close*openPosition.Shares)-(openPosition.Price*openPosition.Shares));
marketValue+=(price.Close*openPosition.Shares);
}
foreach(PortfolioTrade closedPosition in closedPositions)
{
double gainLossPosition=(closedPosition.SellPrice*closedPosition.Shares)-(closedPosition.Price*closedPosition.Shares);
gainLossClosedPositions+=gainLossPosition;
}
double dividendPaymentsToDate=dividendPayments.GetDividendPaymentsToDate(currentDate);
marketValue+=dividendPaymentsToDate;
gainLoss+=dividendPaymentsToDate;
performanceItem.Date=currentDate;
performanceItem.Exposure=exposure;
performanceItem.MarketValue=marketValue;
performanceItem.GainLossDOD=double.IsNaN(prevGainLoss)?gainLoss:(gainLoss-prevGainLoss)+gainLossClosedPositions;
performanceItem.GainLoss=gainLoss+gainLossClosedPositions;
performanceItem.ClosedPositions=closedPositions.Count>0?true:false;
performanceSeries.Add(performanceItem);
prevGainLoss=gainLoss;
}
performanceSeries.CalculatePerformance();
double totalGainLoss=double.NaN;
foreach(ModelPerformanceItem performanceItem in performanceSeries)
{
double dividendPaymentsToDate=dividendPayments.GetDividendPaymentsToDate(performanceItem.Date);
if(double.IsNaN(totalGainLoss))totalGainLoss=performanceItem.GainLossDOD;
else totalGainLoss+=performanceItem.GainLossDOD;
TotalGainLossItem totalGainLossItem=new TotalGainLossItem(
performanceItem.Date,
totalGainLoss,
performanceItem.CumProdMinusOne*100.00,
performanceItem.Exposure,
performanceItem.MarketValue,
dividendPaymentsToDate);
gainLossList.Add(totalGainLossItem);
}
TotalGainLossCollection totalGainLossCollection= new TotalGainLossCollection(gainLossList);
totalGainLossCollection.Sort();
return totalGainLossCollection;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception: {0}",exception.ToString()));
return new TotalGainLossCollection(new List<TotalGainLossItem>());
}
}
public bool ValidatePortfolioTrades(PortfolioTrades portfolioTrades)
{
foreach(PortfolioTrade portfolioTrade in portfolioTrades)
{
if(!dateGenerator.IsMarketOpen(portfolioTrade.TradeDate))return false;
if(!dateGenerator.IsMarketOpen(portfolioTrade.SellDate)) return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,111 @@
using System;
using System.Linq;
using System.Collections.Generic;
using MarketData;
using MarketData.MarketDataModel;
using MarketData.MarketDataModel.GainLoss;
using MarketData.Utils;
using MarketData.DataAccess;
using MarketData.Cache;
namespace MarketData.Generator.GainLoss
{
public class GainLossHelper
{
private GainLossHelper()
{
}
public static double? GetTotalGainLoss(DateTime holdingDate,PortfolioTrade portfolioTrade)
{
if(holdingDate<portfolioTrade.TradeDate) return null;
if(portfolioTrade.IsOpen||(portfolioTrade.IsClosed&&portfolioTrade.SellDate>holdingDate))
{
Price price=LocalPriceCache.GetInstance().GetPrice(portfolioTrade.Symbol,holdingDate);
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No price for {0} on {1}",portfolioTrade.Symbol,Utility.DateTimeToStringMMHDDHYYYY(holdingDate)));
return null;
}
return (price.Close*portfolioTrade.Shares)-(portfolioTrade.Shares*portfolioTrade.Price);
}
return (portfolioTrade.SellPrice*portfolioTrade.Shares)-(portfolioTrade.Price*portfolioTrade.Shares);
}
public static double? GetTotalMarketValue(DateTime holdingDate,PortfolioTrade portfolioTrade)
{
if(holdingDate<portfolioTrade.TradeDate) return null;
if(portfolioTrade.IsOpen||(portfolioTrade.IsClosed&&portfolioTrade.SellDate>holdingDate))
{
Price price=LocalPriceCache.GetInstance().GetPrice(portfolioTrade.Symbol,holdingDate);
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No price for {0} on {1}",portfolioTrade.Symbol,Utility.DateTimeToStringMMHDDHYYYY(holdingDate)));
return null;
}
return (price.Close*portfolioTrade.Shares);
}
return (portfolioTrade.SellPrice*portfolioTrade.Shares);
}
public static double? GetTotalExposure(DateTime holdingDate,PortfolioTrade portfolioTrade)
{
if(holdingDate<portfolioTrade.TradeDate) return null;
return portfolioTrade.Shares*portfolioTrade.Price;
}
public static double? GetTotalCostBasis(DateTime holdingDate,PortfolioTrade portfolioTrade)
{
if(holdingDate<portfolioTrade.TradeDate) return null;
return portfolioTrade.Price*portfolioTrade.Shares;
}
public static double? GetGainLoss(DateTime holdingDate,PortfolioTrade portfolioTrade)
{
if(holdingDate<portfolioTrade.TradeDate) return null;
if(!portfolioTrade.SellDate.Equals(Utility.Epoch)&&holdingDate>portfolioTrade.SellDate) return null;
// check to see if we sold this on holdingDate
if(!portfolioTrade.SellDate.Equals(Utility.Epoch)&&holdingDate.Equals(portfolioTrade.SellDate))
{
return (portfolioTrade.SellPrice*portfolioTrade.Shares)-(portfolioTrade.Price*portfolioTrade.Shares);
}
// check to see if we bought and sold on the same date.
if(portfolioTrade.SellDate.Equals(portfolioTrade.TradeDate))
{
return (portfolioTrade.SellPrice*portfolioTrade.Shares)-(portfolioTrade.Price*portfolioTrade.Shares);
}
Price price=LocalPriceCache.GetInstance().GetPrice(portfolioTrade.Symbol,holdingDate);
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No price for {0} on {1}",portfolioTrade.Symbol,Utility.DateTimeToStringMMHDDHYYYY(holdingDate)));
return null;
}
return (price.Close*portfolioTrade.Shares)-(portfolioTrade.Shares*portfolioTrade.Price);
}
public static double? GetMarketValue(DateTime holdingDate,PortfolioTrade portfolioTrade)
{
if(holdingDate<portfolioTrade.TradeDate) return null;
if(!portfolioTrade.SellDate.Equals(Utility.Epoch)&&holdingDate>portfolioTrade.SellDate) return null;
// check to see if we bought and sold on the same date.
if(portfolioTrade.SellDate.Equals(portfolioTrade.TradeDate)) return (portfolioTrade.SellPrice*portfolioTrade.Shares);
Price price=LocalPriceCache.GetInstance().GetPrice(portfolioTrade.Symbol,holdingDate);
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No price for {0} on {1}",portfolioTrade.Symbol,Utility.DateTimeToStringMMHDDHYYYY(holdingDate)));
return null;
}
return portfolioTrade.Shares*price.Close;
}
public static double? GetCostBasis(DateTime holdingDate,PortfolioTrade portfolioTrade)
{
if(holdingDate<portfolioTrade.TradeDate) return null;
if(!portfolioTrade.SellDate.Equals(Utility.Epoch)&&holdingDate>portfolioTrade.SellDate) return null;
return portfolioTrade.Price*portfolioTrade.Shares;
}
public static double? GetExposure(DateTime holdingDate,PortfolioTrade portfolioTrade)
{
if(holdingDate<portfolioTrade.TradeDate) return null;
if(!portfolioTrade.SellDate.Equals(Utility.Epoch)&&holdingDate>portfolioTrade.SellDate) return null;
return portfolioTrade.Shares*portfolioTrade.Price;
}
}
}

View File

@@ -0,0 +1,12 @@
using System;
using MarketData.MarketDataModel;
using MarketData.MarketDataModel.GainLoss;
namespace MarketData.Generator.GainLoss
{
public interface IActiveGainLossGenerator
{
GainLossCollection GenerateGainLoss(PortfolioTrades portfolioTrades,DateTime? maxDateRef=null);
}
}

View File

@@ -0,0 +1,12 @@
using System;
using MarketData.MarketDataModel;
using MarketData.MarketDataModel.GainLoss;
namespace MarketData.Generator.GainLoss
{
public interface ITotalGainLossGenerator
{
TotalGainLossCollection GenerateTotalGainLoss(PortfolioTrades portfolioTrades,DateTime? maxDateRef=null);
TotalGainLossCollection GenerateTotalGainLossWithDividends(PortfolioTrades portfolioTrades,DividendPayments dividendPayments,DateTime? maxDateRef=null);
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MarketData.Generator
{
public class GrahamGenerator
{
// EPS: Earnings per share
// Growth: eps growth in decimal percent (i.e.) .25 represents 25%
// Benjamin Graham Formula. EPS=Trailing Twelve month, 8.5=PE base for no growth company, g=reasonably expected 7-10 year growth rate
public static double IntrinsicValue(double eps,double growth)
{
return eps * (8.5 + (2 * (growth * 100)));
}
// Grahams revision which includes a required rate of return of 4.4% which is what the going risk free rate was. Today we divied by AAA Corporate Bond Rate
public static double IntrinsicValueRevised(double eps,double growth,double riskFreeRate)
{
double requiredReturn=4.4;
return (eps * (8.5 + (2 * (growth * 100))) * requiredReturn) / riskFreeRate;
}
// The Graham number is a figure that measures a stock's fundamental value by taking into account the company's earnings per share and book value per share.
// The Graham number is the upper bound of the price range that a defensive investor should pay for the stock. According to the theory, any stock price below
// the Graham number is considered undervalued and thus worth investing in. The formula is as follows:
public static double GrahamNumber(double eps,double bvps)
{
return Math.Sqrt(22.5*eps*bvps);
}
}
}

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MarketData.Numerical;
using MarketData.MarketDataModel;
using MarketData.DataAccess;
using MarketData.Cache;
using MarketData.Utils;
namespace MarketData.Generator.Indicators
{
// ***********************************************************************************
public class BandBreakIndicator
{
private BandBreakIndicator()
{
}
// This Indicator determines if a high price exceeds the K band (+2SSD) of the bollinger band over the past "dayCount" days.
// If high price breaks the K band then we consider that to be a true indication.
public static bool IsUpperBandBreakHigh(String symbol,DateTime pricingDate,int dayCount=20)
{
bool isBandBreak=false;
int pricingDays=dayCount*4;
if(pricingDays<60)pricingDays=60;
Prices prices=GBPriceCache.GetInstance().GetPrices(symbol,pricingDate,pricingDays);
if(null==prices || 0==prices.Count)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("*** IsUpperBandBreakHigh: Failed to retrieve prices for {0} on {1} for {2} days. ***",symbol,pricingDate.ToShortDateString(),pricingDays));
return isBandBreak;
}
BollingerBands bollingerBands=BollingerBandGenerator.GenerateBollingerBands(prices);
if(null==bollingerBands)return false;
bollingerBands=new BollingerBands(bollingerBands.Take(dayCount).ToList());
BollingerBandElement item=bollingerBands.Where(x => x.High > x.K).FirstOrDefault();
if(null!=item)isBandBreak=true;
return isBandBreak;
}
// This Indicator determines if a close price is equal to or less than the L band (-2SSD) of the bollinger band over the past "dayCount" days.
// If close price breaks or is equal to the L band then we consider that to be a true indication.
// If there is a violation then the date of the violation is return otherwise we return Epoch
public static DateTime IsLowerBandBreakClose(String symbol,DateTime pricingDate,int dayCount=20)
{
DateTime bandBreakDate=Utility.Epoch;
int pricingDays=dayCount*4;
if(pricingDays<60)pricingDays=60;
Prices prices=GBPriceCache.GetInstance().GetPrices(symbol,pricingDate,pricingDays);
if(null==prices || 0==prices.Count)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("*** IsLowerBandBreakClose: Failed to retrieve prices for {0} on {1} for {2} days. ***",symbol,pricingDate.ToShortDateString(),pricingDays));
return bandBreakDate;
}
BollingerBands bollingerBands=BollingerBandGenerator.GenerateBollingerBands(prices);
if(null==bollingerBands || bollingerBands.Count<dayCount)return bandBreakDate;
bollingerBands=new BollingerBands(bollingerBands.Take(dayCount).ToList());
BollingerBandElement item=bollingerBands.Where(x => x.Close <= x.L).FirstOrDefault();
if(null!=item)
{
bandBreakDate=item.Date;
}
return bandBreakDate;
}
}
}

View File

@@ -0,0 +1,32 @@
using MarketData.MarketDataModel;
using MarketData.Numerical;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// Richard Donchian Channel Breakout. Donchian used 20 days.
namespace MarketData.Generator.Indicators
{
public class ChannelBreakoutIndicator
{
private ChannelBreakoutIndicator()
{
}
public static bool IsChannelBreakOut(DateTime tradeDate,Prices prices,int dayCount)
{
if(null==prices||prices.Count<dayCount||0==dayCount||prices[0].Date!=tradeDate) return false;
return IsChannelBreakOut(prices[0],prices,dayCount);
}
public static bool IsChannelBreakOut(Price currentPrice,Prices prices,int dayCount)
{
if(null==prices||prices.Count<dayCount||null==currentPrice) return false;
prices=new Prices(prices.Take(dayCount).ToList()); // use the dayCount range provided
prices=new Prices(prices.Where(x => !(x.Date.Date.Equals(currentPrice.Date.Date))).ToList()); // make sure currentPrice is not in the list of prices
int count=prices.Count(x => x.Close>=currentPrice.Close); // count how many closing prices are above or equal to our closing price
return 0==count?true:false; // if no records then currentPrice is the max over the period so return true, otherwise return false;
}
}
}

View File

@@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MarketData.Numerical;
using MarketData.MarketDataModel;
using MarketData.DataAccess;
using MarketData.Cache;
using MarketData.Utils;
namespace MarketData.Generator.Indicators
{
// ***********************************************************************************
public class MVPIndicator
{
private static readonly double DAY_COUNT=15.00;
private static readonly double MOMENTUM_MIN_DAYS=12.00;
private MVPIndicator() // Marc Minervini "M"omentum, "V"olume, "P"rice
{
}
public static bool IsTrue(String symbol,DateTime pricingDate)
{
Prices prices=GBPriceCache.GetInstance().GetPrices(symbol,pricingDate,(int)DAY_COUNT+1);
if(null==prices||DAY_COUNT+1>prices.Count) return false;
double daysUp=0;
double daysDown=0;
bool qualifiesOnPrice=false;
bool qualifiesOnVolume=false;
bool qualifiesOnMomentum=false;
// Momentum Check
for(int index=prices.Count-2;index>=0;index--)
{
Price currentPrice=prices[index];
Price prevPrice=prices[index+1];
if(currentPrice.Close>prevPrice.Close)daysUp++;
else daysDown++;
}
if(0.00==daysDown) qualifiesOnMomentum=true;
else qualifiesOnMomentum=daysUp>=MOMENTUM_MIN_DAYS?true:false;
if(!qualifiesOnMomentum) return false;
// Volume Check
double baseVolume=prices[prices.Count-1].Volume;
for(int index=prices.Count-2;index>=0 && !qualifiesOnVolume;index--)
{
Price currentPrice=prices[index];
double percentChangeInVolume=((double)currentPrice.Volume-baseVolume)/baseVolume;
if(percentChangeInVolume>=.25)qualifiesOnVolume=true;
}
if(!qualifiesOnVolume)return false;
// Price check
double cumulativeReturn=prices.GetCumulativeReturn();
if(cumulativeReturn>=.20) qualifiesOnPrice=true;
if(!qualifiesOnPrice) return false;
return true;
}
}
}

View File

@@ -0,0 +1,47 @@
using MarketData.MarketDataModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MarketData.Generator.Indicators
{
public class NarrowRangeIndicator
{
private NarrowRangeIndicator()
{
}
// It is assumed that prices are in descending date order. (i.e.) The most recent price is in the lowest index position
public static bool IsNarrowRangeEntry(DateTime date,Prices prices,int dayCount)
{
if(!IsInsideDay(date,prices))return false;
if(!IsNarrowestRange(date,prices,dayCount))return false;
return true;
}
private static bool IsInsideDay(DateTime date,Prices prices)
{
int index=prices.FindIndex(x=>x.Date.Date.Equals(date));
if(-1==index||index+1>=prices.Count)return false;
if(!(prices[index].High<=prices[index+1].High&&prices[index].Low>=prices[index+1].Low))return false;
return true;
}
private static bool IsNarrowestRange(DateTime date,Prices prices,int dayCount)
{
int index=prices.FindIndex(x => x.Date.Date.Equals(date));
double range=GetRange(prices,index);
if(index+dayCount>=prices.Count-1)return false;
for(int dayIndex=index+1;dayIndex<index+dayCount;dayIndex++)
{
double nextRange=GetRange(prices,dayIndex);
if(nextRange<range)return false;
}
return true;
}
private static double GetRange(Prices prices,int index)
{
double range=prices[index].High-prices[index].Low;
return Math.Abs(range);
}
}
}

View File

@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MarketData.Numerical;
using MarketData.MarketDataModel;
using MarketData.DataAccess;
using MarketData.Cache;
using MarketData.Utils;
namespace MarketData.Generator.Indicators
{
// ***********************************************************************************
public class OverExtendedIndicator
{
private static double marginPercent=1.50; // The default margin is 1.00. 1.50 Gives the best results in backtests from 2013->2021
private OverExtendedIndicator() // Also, regarding VioaltionThresshold below.... the best results were obtained with MarginePercent=1.5 and ViolationThresshol=1.00
{
}
// UseOverExtendedIndicator:true/false
// UseOverExtendedIndicatorDays:45
// UseOverExtendedViolationThreshhold:2 breaks >=2 this number are considered to be violations
// This Indicator determines if a price is overextended by looking at the K band of the bollinger band over the past "dayCount" days.
// If closing price breaks the K band by more than 1.00% then we consider that to be an indication that the price is overextended.
public static bool? IsOverextended(String symbol,DateTime pricingDate,int dayCount=20)
{
bool isOverExtended=false;
Prices prices=GBPriceCache.GetInstance().GetPrices(symbol,pricingDate,dayCount*4);
if(null==prices || 0==prices.Count)return null;
BollingerBands bollingerBands=BollingerBandGenerator.GenerateBollingerBands(prices,dayCount);
if(null==bollingerBands)return false;
bollingerBands=new BollingerBands(bollingerBands.Take(dayCount).ToList());
BollingerBandElement item=bollingerBands.Where(x => x.Close>x.K && (((x.Close-x.K)/x.K)*100.00)>marginPercent).FirstOrDefault();
if(null!=item)isOverExtended=true;
return isOverExtended;
}
// This method considers an overextended condition based upon an allowable number of upper band breaks
public static bool? IsOverextended(String symbol,DateTime pricingDate,int dayCount=20,int violationThreshhold=1)
{
bool isOverExtended=false;
Prices prices=GBPriceCache.GetInstance().GetPrices(symbol,pricingDate,dayCount*4);
if(null==prices||0==prices.Count) return null;
BollingerBands bollingerBands=BollingerBandGenerator.GenerateBollingerBands(prices,dayCount);
if(null==bollingerBands) return false;
bollingerBands=new BollingerBands(bollingerBands.Take(dayCount).ToList());
List<BollingerBandElement> items=bollingerBands.Where(x => x.Close>x.K&&(((x.Close-x.K)/x.K)*100.00)>marginPercent).ToList();
if(null==items)return null;
if(items.Count>violationThreshhold)isOverExtended=true;
return isOverExtended;
}
// This method considers an overextended condition based upon an allowable number of upper band breaks and user supplied marginPercent
public static bool? IsOverextended(String symbol,DateTime pricingDate,int dayCount=20,int violationThreshhold=1,double paramMarginPercent=1.00)
{
bool isOverExtended=false;
Prices prices=GBPriceCache.GetInstance().GetPrices(symbol,pricingDate,dayCount*4);
if(null==prices||0==prices.Count) return null;
BollingerBands bollingerBands=BollingerBandGenerator.GenerateBollingerBands(prices,dayCount);
if(null==bollingerBands) return false;
bollingerBands=new BollingerBands(bollingerBands.Take(dayCount).ToList());
List<BollingerBandElement> items=bollingerBands.Where(x => x.Close>x.K&&(((x.Close-x.K)/x.K)*100.00)>paramMarginPercent).ToList();
if(null==items) return null;
if(items.Count>violationThreshhold) isOverExtended=true;
return isOverExtended;
}
public static int? OverExtendedCount(String symbol,DateTime pricingDate,int dayCount=20)
{
Prices prices=GBPriceCache.GetInstance().GetPrices(symbol,pricingDate,dayCount*4);
if(null==prices||0==prices.Count) return null;
BollingerBands bollingerBands=BollingerBandGenerator.GenerateBollingerBands(prices,dayCount);
if(null==bollingerBands) return null;
bollingerBands=new BollingerBands(bollingerBands.Take(dayCount).ToList());
List<BollingerBandElement> items=bollingerBands.Where(x => x.Close>x.K).ToList();
if(null==items)return null;
return items.Count;
}
}
}

View File

@@ -0,0 +1,44 @@
using MarketData.MarketDataModel;
using MarketData.Numerical;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MarketData.Generator.Indicators
{
public class PriceTrendIndicatorResult
{
public PriceTrendIndicatorResult(bool isUptrend,double highPriceSlope=double.NaN,double lowPriceSlope=double.NaN)
{
this.IsUpTrend=isUptrend;
this.HighPriceSlope=highPriceSlope;
this.LowPriceSlope=lowPriceSlope;
}
public bool IsUpTrend{get;private set;}
public double HighPriceSlope{get;private set;}
public double LowPriceSlope{get; private set;}
}
public class PriceTrendIndicator
{
private PriceTrendIndicator()
{
}
public static PriceTrendIndicatorResult IsUptrend(Prices prices,int dayCount)
{
if(null==prices||prices.Count<dayCount) return new PriceTrendIndicatorResult(false);
float[] highPrices=prices.GetPricesHigh();
highPrices=highPrices.Take(dayCount).ToArray();
highPrices=Numerics.Reverse(ref highPrices);
double highPricesSlope=Numerics.Slope(highPrices);
if(highPricesSlope<0) return new PriceTrendIndicatorResult(false);
float[] lowPrices=prices.GetPricesLow();
lowPrices=lowPrices.Take(dayCount).ToArray();
lowPrices=Numerics.Reverse(ref lowPrices);
double lowPricesSlope=Numerics.Slope(lowPrices);
if(lowPricesSlope<0) return new PriceTrendIndicatorResult(false);
return new PriceTrendIndicatorResult(true,highPricesSlope,lowPricesSlope);
}
}
}

View File

@@ -0,0 +1,26 @@
using MarketData.MarketDataModel;
using MarketData.Numerical;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MarketData.Generator.Indicators
{
public class VolumeTrendIndicator
{
private VolumeTrendIndicator()
{
}
public static bool IsUptrend(Prices prices,int dayCount)
{
if(null==prices||prices.Count<dayCount) return false;
float[] volume=prices.GetVolume();
volume=volume.Take(dayCount).ToArray();
volume=Numerics.Reverse(ref volume);
double volumeSlope=Numerics.Slope(volume);
return volumeSlope>0.00?true:false;
}
}
}

View File

@@ -0,0 +1,13 @@
using System;
namespace MarketData.Generator.Interface
{
public interface IPosition : IPurePosition
{
double TrailingStopLimit {get; set;}
double InitialStopLimit {get; set;}
double PositionRiskPercentDecimal {get; set;}
}
}

View File

@@ -0,0 +1,19 @@
using System;
namespace MarketData.Generator.Interface
{
public interface IPurePosition
{
String Symbol {get; set;}
DateTime PurchaseDate {get; set;}
DateTime SellDate {get; set;}
double CurrentPrice {get; set;}
double PurchasePrice {get; set;}
double Shares {get; set;}
}
}

View File

@@ -0,0 +1,131 @@
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;
using System.Reflection;
//using System.Runtime.Remoting.Messaging;
using System.Text;
using MarketData.MarketDataModel;
using MarketData.DataAccess;
using MarketData.Utils;
using MarketData.Numerical;
using MarketData;
namespace MarketData.Generator
{
public class MACDGenerator
{
private MACDGenerator()
{
}
public static MACDFastSlowSignals GenerateMACD(String symbol,int dayCount=120)
{
try
{
DateGenerator dateGenerator = new DateGenerator();
DateTime startDate = DateTime.Now;
if (null == symbol) return null;
symbol = symbol.ToUpper();
Dictionary<DateTime, MACDFastSlowSignal> macdFastSlowSignalsByDate = new Dictionary<DateTime, MACDFastSlowSignal>();
startDate = dateGenerator.GetPrevBusinessDay(DateTime.Now); ;
if (Utility.IsEpoch(startDate))
{
MDTrace.WriteLine(LogLevel.DEBUG,"No price dates for '" + symbol + "'");
return null;
}
DateTime historicalDate = dateGenerator.GenerateHistoricalDate(startDate, dayCount);
Prices prices = PricingDA.GetPrices(symbol, startDate, historicalDate);
if (null == prices || 0 == prices.Count)
{
MDTrace.WriteLine(LogLevel.DEBUG,"No price dates for '" + symbol + "'");
return null;
}
Price latestPrice = prices[0];
String companyName = PricingDA.GetNameForSymbol(symbol);
MACDSetup macdSetupSlow = new MACDSetup(12, 26, 9);
MACDSetup macdSetupFast = new MACDSetup(8, 17, 9);
MACDSignals macdSignalsSlow = MACDGenerator.GenerateMACD(prices, macdSetupSlow);
MACDSignals macdSignalsFast = MACDGenerator.GenerateMACD(prices, macdSetupFast);
Signals signalsSlow=SignalGenerator.GenerateSignals(macdSignalsSlow);
signalsSlow=signalsSlow.CondenseSignals();
Signals signalsFast=SignalGenerator.GenerateSignals(macdSignalsFast);
signalsFast=signalsFast.CondenseSignals();
for (int index = 0; index < macdSignalsSlow.Count; index++)
{
MACDSignal macdSignal = macdSignalsSlow[index];
if (!macdFastSlowSignalsByDate.ContainsKey(macdSignal.Date))
{
MACDFastSlowSignal macdFastSlowSignal = new MACDFastSlowSignal();
macdFastSlowSignal.Date = macdSignal.Date;
macdFastSlowSignal.SignalSlow = macdSignal.Signal;
macdFastSlowSignal.MACDSlow = macdSignal.MACD;
macdFastSlowSignalsByDate.Add(macdSignal.Date, macdFastSlowSignal);
}
}
for (int index = 0; index < macdSignalsFast.Count; index++)
{
MACDSignal macdSignal = macdSignalsFast[index];
if (!macdFastSlowSignalsByDate.ContainsKey(macdSignal.Date)) continue;
MACDFastSlowSignal macdFastSlowSignal = macdFastSlowSignalsByDate[macdSignal.Date];
macdFastSlowSignal.SignalFast = macdSignal.Signal;
macdFastSlowSignal.MACDFast = macdSignal.MACD;
}
MACDFastSlowSignals macdFastSlowSignals = new MACDFastSlowSignals(macdFastSlowSignalsByDate.Values);
macdFastSlowSignals.Symbol = symbol;
macdFastSlowSignals.FromDate = prices[prices.Count - 1].Date;
macdFastSlowSignals.ThruDate = prices[0].Date;
macdFastSlowSignals.Sort(new MACDFastSlowSignalsComparerAsc());
macdFastSlowSignals.SlowCondensedSignals=signalsSlow;
macdFastSlowSignals.FastCondensedSignals=signalsFast;
return macdFastSlowSignals;
}
catch (Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception);
return null;
}
}
public static MACDSignals GenerateMACD(Prices prices, MACDSetup macdSetup)
{
try
{
MACDSignals macdSignals = new MACDSignals();
for (int index = 0; index < prices.Count; index++)
{
MACDSignal macdSignal = new MACDSignal();
float[] pricesArray = null;
Price price = prices[index];
macdSignal.Symbol = price.Symbol;
macdSignal.Date = price.Date;
pricesArray = prices.GetPrices(index, macdSetup.Fast); // 12
if (null == pricesArray) break;
macdSignal.Fast = Numerics.Mean(ref pricesArray);
pricesArray = prices.GetPrices(index, macdSetup.Slow);
if (null == pricesArray) break;
macdSignal.Slow = Numerics.Mean(ref pricesArray); // 26
macdSignal.MACD = macdSignal.Fast - macdSignal.Slow;
macdSignals.Add(macdSignal);
}
for (int index = 0; index < macdSignals.Count; index++)
{
MACDSignal macdSignal = macdSignals[index];
float[] macd = macdSignals.GetMACD(index, macdSetup.Signal); // 9
if (null != macd) macdSignal.Signal = Numerics.Mean(ref macd);
macdSignal.Histogram = macdSignal.MACD - macdSignal.Signal;
}
return macdSignals;
}
catch (Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception);
return null;
}
}
}
}

View File

@@ -0,0 +1,24 @@
using MarketData.DataAccess;
using MarketData.MarketDataModel;
using MarketData.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
namespace MarketData.Generator.Model
{
public class ModelStatistics
{
public long TotalTrades{get;set;} // The total number of trades
public long WinningTrades{get;set;} // The number of winning trades
public long LosingTrades{get;set;} // The number of losing trades
public double AverageWinningTradePercentGain{get;set;} // Average percent gain of the winning trades
public double AverageLosingTradePercentLoss{get;set;} // Average percent loss of the losing trades
public double WinningTradesPercent{get;set;} // Percentage of trades that are winners
public double LosingTradesPercent{get;set;} // Percentage of trades that are losers
public double Expectancy { get; set; } // The Expectancy. (WinningTradesPercent * AverageWinningTradePercent)/(percent losing trades * average loss) We are looking for a number greater than zero.
}
}

View File

@@ -0,0 +1,128 @@
using MarketData.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// This StopLimit class is used by models and the UI
/// </summary>
namespace MarketData.Generator.Model
{
public class StopLimits:List<StopLimit>
{
public StopLimits()
{
}
public StopLimits(List<StopLimit> stopLimits)
{
foreach(StopLimit stopLimit in stopLimits)Add(stopLimit);
}
public NVPCollections ToNVPCollections()
{
NVPCollections nvpCollections=new NVPCollections();
foreach(StopLimit stopLimit in this)
{
nvpCollections.Add(stopLimit.ToNVPCollection());
}
return nvpCollections;
}
public static StopLimits FromNVPCollections(NVPCollections nvpCollections)
{
StopLimits stopLimits=new StopLimits();
foreach(NVPCollection nvpCollection in nvpCollections)
{
stopLimits.Add(StopLimit.FromNVPCollection(nvpCollection));
}
return stopLimits;
}
public void AddFromNVPCollection(NVPCollection nvpCollection)
{
Add(StopLimit.FromNVPCollection(nvpCollection));
}
}
public class StopLimit
{
public StopLimit()
{
}
public StopLimit(StopLimit stopLimit)
{
this.Symbol=stopLimit.Symbol;
this.AnalysisDate=stopLimit.AnalysisDate;
this.PreviousStop=stopLimit.PreviousStop;
this.NewStop=stopLimit.NewStop;
this.CurrentPriceLow=stopLimit.CurrentPriceLow;
this.CurrentPriceClose=stopLimit.CurrentPriceClose;
this.PriceTrendIndicatorSlope=stopLimit.PriceTrendIndicatorSlope;
this.StopLimitId=stopLimit.StopLimitId;
}
public StopLimit(String symbol,DateTime analysisDate,double previousStop,double newStop,double currentPriceLow,double currentPriceClose,double priceTrendIndicatorSlope)
{
this.Symbol=symbol;
this.AnalysisDate=analysisDate;
this.PreviousStop=previousStop;
this.NewStop=newStop;
this.CurrentPriceLow=currentPriceLow;
this.CurrentPriceClose=currentPriceClose;
this.PriceTrendIndicatorSlope=priceTrendIndicatorSlope;
}
public String Symbol { get; set; }
public DateTime AnalysisDate{get;set;}
public double PreviousStop{get;set;}
public double NewStop{get;set;}
public double CurrentPriceLow{get;set;}
public double CurrentPriceClose{get;set;}
public double PriceTrendIndicatorSlope{get;set;}
public String StopLimitId {get;set;}
public static String Header()
{
StringBuilder sb=new StringBuilder();
sb.Append("Id,Symbol,AnalysisDate,PreviousStop,NewStop,CurrentPriceLow,CurrentPriceClose,PriceTrendIndicatorSlope");
return sb.ToString();
}
public override String ToString()
{
StringBuilder sb=new StringBuilder();
sb.Append(null==StopLimitId?"":StopLimitId).Append(",");
sb.Append(Symbol).Append(",");
sb.Append(AnalysisDate.ToShortDateString()).Append(",");
sb.Append(Utility.FormatCurrency(PreviousStop)).Append(",");
sb.Append(Utility.FormatCurrency(NewStop)).Append(",");
sb.Append(Utility.FormatCurrency(CurrentPriceLow)).Append(",");
sb.Append(Utility.FormatCurrency(CurrentPriceClose)).Append(",");
sb.Append(Utility.FormatNumber(PriceTrendIndicatorSlope,4,false));
return sb.ToString();
}
public virtual NVPCollection ToNVPCollection()
{
NVPCollection nvpCollection=new NVPCollection();
nvpCollection.Add(new NVP("Symbol",Symbol.ToString()));
nvpCollection.Add(new NVP("AnalysisDate",AnalysisDate.ToString()));
nvpCollection.Add(new NVP("PreviousStop",PreviousStop.ToString()));
nvpCollection.Add(new NVP("NewStop",NewStop.ToString()));
nvpCollection.Add(new NVP("CurrentPriceLow",CurrentPriceLow.ToString()));
nvpCollection.Add(new NVP("CurrentPriceClose",CurrentPriceClose.ToString()));
nvpCollection.Add(new NVP("PriceTrendIndicatorSlope",PriceTrendIndicatorSlope.ToString()));
nvpCollection.Add(new NVP("StopLimitId",StopLimitId==null?"":StopLimitId));
return nvpCollection;
}
public static StopLimit FromNVPCollection(NVPCollection nvpCollection)
{
StopLimit stopLimit=new StopLimit();
NVPDictionary nvpDictionary=nvpCollection.ToDictionary();
stopLimit.Symbol=nvpDictionary["Symbol"].Get<String>();
stopLimit.AnalysisDate=nvpDictionary["AnalysisDate"].Get<DateTime>();
stopLimit.PreviousStop=nvpDictionary["PreviousStop"].Get<double>();
stopLimit.NewStop=nvpDictionary["NewStop"].Get<double>();
stopLimit.CurrentPriceLow=nvpDictionary["CurrentPriceLow"].Get<double>();
stopLimit.CurrentPriceClose=nvpDictionary["CurrentPriceClose"].Get<double>();
stopLimit.PriceTrendIndicatorSlope=nvpDictionary["PriceTrendIndicatorSlope"].Get<double>();
if(nvpDictionary.ContainsKey("StopLimitId"))stopLimit.StopLimitId=nvpDictionary["StopLimitId"].Get<String>();
return stopLimit;
}
}
}

View File

@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using MarketData.MarketDataModel;
using MarketData.Utils;
using MarketData.DataAccess;
using MarketData.Numerical;
using MarketData.Cache;
namespace MarketData.Generator.ModelGenerators
{
public class EdgeRatioGenerator
{
private EdgeRatioGenerator()
{
}
public static EdgeRatioResult CalculateEdgeRatio(String symbol,DateTime purchaseDate,double purchasePrice,DateTime asOf)
{
EdgeRatioResult edgeRatioResult=new EdgeRatioResult();
DateGenerator dateGenerator=new DateGenerator();
DateTime lastBusinesDate=dateGenerator.GetPrevBusinessDay(asOf);
Dictionary<DateTime,TrueRangeResult> trueRangeResults=new Dictionary<DateTime,TrueRangeResult>();
double[] mae=null;
double[] mfe=null;
double[] atr=null;
try
{
List<DateTime> seriesDates=dateGenerator.GenerateHistoricalDates(purchaseDate,asOf);
mae=new double[seriesDates.Count];
mfe=new double[seriesDates.Count];
atr=new double[seriesDates.Count];
double sumMAEPlusATR=0.00;
double sumMFEPlusATR=0.00;
double averageMAE=0.00;
double averageMFE=0.00;
asOf=dateGenerator.GetPrevBusinessDay(asOf);
for(int index=0;index<seriesDates.Count;index++)
{
DateTime seriesDate=seriesDates[index];
Price seriesPrice=null;
if(seriesDate.Date.Equals(lastBusinesDate.Date))seriesPrice=PricingDA.GetPrice(symbol,seriesDate); // ensure we always get a hot price for last business date, other price may originate from cache
else seriesPrice=GBPriceCache.GetInstance().GetPrice(symbol,seriesDate);
if(null==seriesPrice)
{
edgeRatioResult.Message=String.Format("Price not available for {0} on {1}",symbol,seriesDate.ToShortDateString());
return edgeRatioResult;
}
mfe[index]=(seriesPrice.High-purchasePrice)<0.00?0.00:(seriesPrice.High-purchasePrice);
mae[index]=(purchasePrice-seriesPrice.Low)<0.00?0.00:(purchasePrice-seriesPrice.Low);
atr[index]=TrueRangeGenerator.GenerateAverageTrueRange(symbol,seriesDate).AverageTrueRange;
}
for(int index=0;index<seriesDates.Count;index++)
{
sumMAEPlusATR+=(mae[index]+atr[index]);
sumMFEPlusATR+=(mfe[index]+atr[index]);
}
averageMAE=sumMAEPlusATR/(double)seriesDates.Count;
averageMFE=sumMFEPlusATR/(double)seriesDates.Count;
edgeRatioResult.EdgeRatio=averageMFE/averageMAE;
edgeRatioResult.Success=true;
edgeRatioResult.Message=String.Format("EdgeRatio for {0} purchased on {1} @ {2} from {3} -> {4}",symbol,purchaseDate.ToShortDateString(),Utility.FormatCurrency(purchasePrice),purchaseDate.ToShortDateString(),asOf.ToShortDateString());
return edgeRatioResult;
}
catch(Exception exception)
{
edgeRatioResult.Message=exception.ToString();
return edgeRatioResult;
}
}
}
public class EdgeRatioResult
{
public EdgeRatioResult()
{
Success=false;
EdgeRatio=double.NaN;
Message=null;
}
public double EdgeRatio{get;set;}
public bool Success{get;set;}
public String Message{get;set;}
}
}

View File

@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using MarketData.MarketDataModel;
using MarketData.Utils;
using MarketData.DataAccess;
using MarketData.Numerical;
namespace MarketData.Generator.ModelGenerators
{
public class TrueRangeGenerator
{
private TrueRangeGenerator()
{
}
// Prices : Where most recent price is at lowest index
public static TrueRangeResult GenerateAverageTrueRange(String symbol, DateTime startDate, int atrDays = 14)
{
Prices prices = PricingDA.GetPrices(symbol, startDate, atrDays + 1);
if (null == prices || prices.Count < 2 || 0 == prices[0].Close) return null;
double[] values = new double[prices.Count - 1];
for (int index = 0; index < prices.Count - 1; index++)
{
Price price = prices[index];
Price prevPrice = prices[index + 1];
double levelA = price.High - price.Low;
double levelB = Math.Abs(price.High - prevPrice.Close);
double levelC = Math.Abs(price.Low - prevPrice.Close);
values[index] = Math.Max(levelA, levelB);
values[index] = Math.Max(values[index], levelC);
}
double averageTrueRange = Numerics.Mean(ref values);
return new TrueRangeResult(averageTrueRange, (averageTrueRange / prices[0].Close) * 2.00);
}
}
public class TrueRangeResult
{
public TrueRangeResult()
{
}
public TrueRangeResult(double averageTrueRange,double shortTermVolatility)
{
AverageTrueRange=averageTrueRange;
ShortTermVolatility=shortTermVolatility;
}
public double AverageTrueRange{get;set;}
public double ShortTermVolatility{get;set;}
}
}

View File

@@ -0,0 +1,50 @@
using MarketData.Generator.MovingAverage;
using MarketData.MarketDataModel;
using MarketData.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MarketData.Generator.ModelGenerators
{
// Calculates Volatility as 3x Average True Range
public class VolatilityGenerator
{
public static double CalculateVolatility(String symbol,DateTime date,int movingAverageDays=10)
{
return CalculateVolatility(symbol,date,movingAverageDays,3.00);
}
public static double CalculateVolatility(String symbol,DateTime date,int movingAverageDays,double atrMultiplier)
{
double volatility=double.NaN;
DateGenerator dateGenerator=new DateGenerator();
Dictionary<DateTime,double> trueRangeCollectionByDate=new Dictionary<DateTime,double>();
List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(date,(movingAverageDays+1)*2); // most recent date in lowest index, we need to generate (items+1)
for(int dateIndex=0;dateIndex<historicalDates.Count;dateIndex++)
{
DateTime historicalDate=historicalDates[dateIndex];
TrueRangeResult trueRangeResult=TrueRangeGenerator.GenerateAverageTrueRange(symbol,historicalDate);
if(null==trueRangeResult)continue;
trueRangeCollectionByDate.Add(historicalDate,trueRangeResult.AverageTrueRange);
}
if(movingAverageDays>trueRangeCollectionByDate.Count) return volatility;
historicalDates=new List<DateTime>(trueRangeCollectionByDate.Keys.OrderByDescending(x=>x.Date));
Prices prices=new Prices();
foreach(DateTime historicalDate in historicalDates)
{
Price price=new Price();
price.Symbol=symbol;
price.Date=historicalDate;
price.Close=trueRangeCollectionByDate[historicalDate];
prices.Add(price);
}
DMAPrices emaPrices=MovingAverageGenerator.GenerateExponentialMovingAverage(prices,movingAverageDays);
if(null==emaPrices||0==emaPrices.Count)return volatility;
volatility=emaPrices[0].AVGPrice*atrMultiplier;
return volatility;
}
}
}

View File

@@ -0,0 +1,212 @@
using MarketData.Cache;
using MarketData.MarketDataModel;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MarketData.Generator.MovingAverage
{
public static class ExponentialMovingAverageCrossover
{
public static MovingAverageCrossovers GetMovingAverageCrossovers(String symbol, DateTime analysisDate,double threshholdPercentDecimal=.05, int fastCrossOverDays=9, int slowCrossoverDays=41)
{
Prices prices = GBPriceCache.GetInstance().GetPrices(symbol, analysisDate, slowCrossoverDays * 6);
if(prices == null || 0==prices.Count)return new MovingAverageCrossovers();
return GetMovingAverageCrossovers(prices, threshholdPercentDecimal, fastCrossOverDays, slowCrossoverDays);
}
/// <summary>
/// GetMovingAverageCrossovers
/// Prices will be in descending order with the most recent price at index[0] and the least recent price at index[count-1]
/// </summary>
/// <param name="prices"></param>
/// <param name="fastCrossOverDays">The fast crossover. Tom Basso uses 9</param>
/// <param name="slowCrossoverDays">The slow crosser. Tom Basso uses 41</param>
/// <param name="threshholdPercent">The threshhold percent. 5% would be .05</param>
/// <returns></returns>
public static MovingAverageCrossovers GetMovingAverageCrossovers(Prices prices, double threshholdPercentDecimal=.05, int fastCrossOverDays=9, int slowCrossoverDays=41)
{
MovingAverageCrossovers movingAverageCrossovers = new MovingAverageCrossovers();
if(prices == null || 0==prices.Count || prices.Count<=slowCrossoverDays)return movingAverageCrossovers;
if(fastCrossOverDays >= slowCrossoverDays)return movingAverageCrossovers;
DMAPrices fastDMAPrices = MovingAverageGenerator.GenerateExponentialMovingAverage(prices, fastCrossOverDays);
DMAPrices slowDMAPrices = MovingAverageGenerator.GenerateExponentialMovingAverage(prices, slowCrossoverDays);
if(null == fastDMAPrices || 0 == fastDMAPrices.Count)return movingAverageCrossovers;
if(null == slowDMAPrices || 0 == slowDMAPrices.Count)return movingAverageCrossovers;
FindCrossovers(fastDMAPrices, slowDMAPrices, movingAverageCrossovers, threshholdPercentDecimal);
// put the most recent item in the lowest index
movingAverageCrossovers = new MovingAverageCrossovers((movingAverageCrossovers as List<MovingAverageCrossover>).Where(x => x.IsChangeInTrend==true).OrderByDescending(x => x.EventDate).ToList());
return movingAverageCrossovers;
}
/// <summary>
/// If the sorter term (faster) crossed the longer term (slower) moving average then a direction change is noted.
/// If the change carried more than 5% in that direction then consider it a successful trend
/// </summary>
/// <param name="fastDMAPrices"></param>
/// <param name="slowDMAPrices"></param>
/// <param name="movingAverageCrossovers"></param>
private static void FindCrossovers(DMAPrices fastDMAPrices, DMAPrices slowDMAPrices,MovingAverageCrossovers movingAverageCrossovers, double threshholdPercentDecimal)
{
double trendChangePercentThreshhold = threshholdPercentDecimal*100.00; // if a change in direction carries more than 5% then consider is a successful trend
movingAverageCrossovers.Clear();
DateTime startDate = slowDMAPrices[slowDMAPrices.Count-1].Date;
MovingAverageCrossover movingAverageCrossover = default;
DMAPricesByDate fastDMAPricesByDate = fastDMAPrices.GetDMAPricesByDate();
DMAPricesByDate slowDMAPricesByDate = slowDMAPrices.GetDMAPricesByDate();
List<DateTime> availableDates = new List<DateTime>(slowDMAPricesByDate.Keys);
availableDates.Sort((a, b) => DateTime.Compare(a,b)); // earliest date should be in the lowest index, most recent date should be in the highest 2024[0], 2025[1], 2026[2] for example
int currentDirection=0; // 1:fast DMA is above the slow, -1:fast DMA is below the slow, 0:slow DMA == fastDMA
DMAPrice prevSlowDMAPrice = default;
DMAPrice prevFastDMAPrice = default;
for(int index=0;index<availableDates.Count;index++)
{
DateTime analysisDate = availableDates[index];
DMAPrice slowDMAPrice = slowDMAPricesByDate[analysisDate];
DMAPrice fastDMAPrice = fastDMAPricesByDate[analysisDate];
if(index == 0) // establish an initial orientation of the fast and slow moving averages. We need to create a new record to have a current trend
{
if(fastDMAPrice.AVGPrice > slowDMAPrice.AVGPrice)
{
movingAverageCrossover = new MovingAverageCrossover();
movingAverageCrossover.EventDate = analysisDate;
movingAverageCrossover.Symbol = slowDMAPrice.Symbol;
movingAverageCrossover.CrossOverDirection = MovingAverageCrossover.CrossoverDirectionEnum.UpStart;
movingAverageCrossover.IsChangeInTrend = false;
movingAverageCrossover.SlowDMAPrice = slowDMAPrice;
movingAverageCrossover.FastDMAPrice = fastDMAPrice;
movingAverageCrossover.ChangePercent=0.00;
movingAverageCrossovers.Add(movingAverageCrossover);
currentDirection = 1;
}
else if(fastDMAPrice.AVGPrice < slowDMAPrice.AVGPrice)
{
movingAverageCrossover = new MovingAverageCrossover();
movingAverageCrossover.EventDate = analysisDate;
movingAverageCrossover.Symbol = slowDMAPrice.Symbol;
movingAverageCrossover.CrossOverDirection = MovingAverageCrossover.CrossoverDirectionEnum.DownStart;
movingAverageCrossover.IsChangeInTrend = false;
movingAverageCrossover.SlowDMAPrice = slowDMAPrice;
movingAverageCrossover.FastDMAPrice = fastDMAPrice;
movingAverageCrossover.ChangePercent=0.00;
movingAverageCrossovers.Add(movingAverageCrossover);
currentDirection = -1;
}
else
{
movingAverageCrossover = new MovingAverageCrossover();
movingAverageCrossover.EventDate = analysisDate;
movingAverageCrossover.Symbol = slowDMAPrice.Symbol;
movingAverageCrossover.CrossOverDirection = MovingAverageCrossover.CrossoverDirectionEnum.NeutralStart;
movingAverageCrossover.IsChangeInTrend = false;
movingAverageCrossover.SlowDMAPrice = slowDMAPrice;
movingAverageCrossover.FastDMAPrice = fastDMAPrice;
movingAverageCrossover.ChangePercent=0.00;
movingAverageCrossovers.Add(movingAverageCrossover);
currentDirection = 0;
}
prevSlowDMAPrice = slowDMAPrice;
prevFastDMAPrice = fastDMAPrice;
}
else
{
if(fastDMAPrice.AVGPrice > slowDMAPrice.AVGPrice)
{
if(1 == currentDirection) // The fast is above the slow as it was previously
{
if(movingAverageCrossovers.Count>0) // check the last change record for a threshhold break
{
MovingAverageCrossover movingAverageCrossOver = movingAverageCrossovers[movingAverageCrossovers.Count-1];
double changePercent = ((fastDMAPrice.AVGPrice - movingAverageCrossOver.FastDMAPrice.AVGPrice)/movingAverageCrossOver.FastDMAPrice.AVGPrice)*100.00;
if(changePercent > 0.00 && changePercent > trendChangePercentThreshhold) // if the threshhold is exceeded then create a new record and mark it as a trend change
{
movingAverageCrossover = new MovingAverageCrossover();
movingAverageCrossover.EventDate = analysisDate;
movingAverageCrossover.Symbol = slowDMAPrice.Symbol;
movingAverageCrossover.CrossOverDirection = MovingAverageCrossover.CrossoverDirectionEnum.UpCross;
movingAverageCrossover.IsChangeInTrend = true;
movingAverageCrossover.SlowDMAPrice = slowDMAPrice;
movingAverageCrossover.FastDMAPrice = fastDMAPrice;
movingAverageCrossover.ChangePercent=changePercent;
movingAverageCrossovers.Add(movingAverageCrossover);
}
}
}
else if(-1 == currentDirection || 0 == currentDirection) // The fast is above the slow and it was below the slow previously
{
currentDirection = 1; // switch directions
movingAverageCrossover = new MovingAverageCrossover();
movingAverageCrossover.EventDate = analysisDate;
movingAverageCrossover.Symbol = slowDMAPrice.Symbol;
movingAverageCrossover.CrossOverDirection = MovingAverageCrossover.CrossoverDirectionEnum.UpCross;
movingAverageCrossover.SlowDMAPrice = slowDMAPrice;
movingAverageCrossover.FastDMAPrice = fastDMAPrice;
double changePercent = Math.Abs(((fastDMAPrice.AVGPrice - slowDMAPrice.AVGPrice)/slowDMAPrice.AVGPrice)*100.00);
movingAverageCrossover.ChangePercent=changePercent;
if(changePercent>trendChangePercentThreshhold)movingAverageCrossover.IsChangeInTrend = true;
else movingAverageCrossover.IsChangeInTrend = false;
movingAverageCrossovers.Add(movingAverageCrossover);
}
}
else if(fastDMAPrice.AVGPrice < slowDMAPrice.AVGPrice)
{
if(-1 == currentDirection) // The fast is below the slow as it was previously
{
if(movingAverageCrossovers.Count>0) // check the last change record for a threshhold break
{
movingAverageCrossover = movingAverageCrossovers[movingAverageCrossovers.Count-1];
double changePercent = Math.Abs(((fastDMAPrice.AVGPrice - movingAverageCrossover.FastDMAPrice.AVGPrice)/movingAverageCrossover.FastDMAPrice.AVGPrice)*100.00);
if(changePercent<0.00 && Math.Abs(changePercent)>trendChangePercentThreshhold) // if the threshhold is exceeded then create a new record and mark it as a trend change
{
movingAverageCrossover = new MovingAverageCrossover();
movingAverageCrossover.EventDate = analysisDate;
movingAverageCrossover.Symbol = slowDMAPrice.Symbol;
movingAverageCrossover.CrossOverDirection = MovingAverageCrossover.CrossoverDirectionEnum.UpCross;
movingAverageCrossover.IsChangeInTrend = true;
movingAverageCrossover.SlowDMAPrice = slowDMAPrice;
movingAverageCrossover.FastDMAPrice = fastDMAPrice;
movingAverageCrossover.ChangePercent = changePercent;
movingAverageCrossovers.Add(movingAverageCrossover);
}
}
}
else if(1 == currentDirection || 0 == currentDirection) // The fast is below the slow but it was above the slow previously so we've crossed
{
currentDirection = -1; // switch directions
movingAverageCrossover = new MovingAverageCrossover();
movingAverageCrossover.EventDate = analysisDate;
movingAverageCrossover.Symbol = slowDMAPrice.Symbol;
movingAverageCrossover.CrossOverDirection = MovingAverageCrossover.CrossoverDirectionEnum.DownCross;
movingAverageCrossover.SlowDMAPrice = slowDMAPrice;
movingAverageCrossover.FastDMAPrice = fastDMAPrice;
double changePercent = Math.Abs(((fastDMAPrice.AVGPrice - slowDMAPrice.AVGPrice)/slowDMAPrice.AVGPrice)*100.00);
movingAverageCrossover.ChangePercent=changePercent;
if(changePercent>trendChangePercentThreshhold)movingAverageCrossover.IsChangeInTrend = true;
else movingAverageCrossover.IsChangeInTrend = false;
movingAverageCrossovers.Add(movingAverageCrossover);
}
}
else // The fast and the slow are equal. There's nothing to do here
{
}
prevSlowDMAPrice = slowDMAPrice;
prevFastDMAPrice = fastDMAPrice;
}
}
}
}
}

View File

@@ -0,0 +1,47 @@
using MarketData.MarketDataModel;
using MarketData.Utils;
using System;
using System.Collections.Generic;
using System.Text;
namespace MarketData.Generator.MovingAverage
{
public class MovingAverageCrossovers : List<MovingAverageCrossover>
{
public MovingAverageCrossovers()
{
}
public MovingAverageCrossovers(List<MovingAverageCrossover> movingAverageCrossovers)
{
foreach(MovingAverageCrossover movingAverageCrossover in movingAverageCrossovers)
{
Add(movingAverageCrossover);
}
}
}
public class MovingAverageCrossover
{
public enum CrossoverDirectionEnum{UpCross, DownCross, UpStart, DownStart, NeutralStart};
public String Symbol { get; set; }
public DateTime EventDate { get; set; }
public CrossoverDirectionEnum CrossOverDirection {get; set;}
public bool IsChangeInTrend { get; set; }
public double ChangePercent { get; set; }
public DMAPrice SlowDMAPrice { get; set; } // the slow DMA price associated with this crossover
public DMAPrice FastDMAPrice { get; set; } // the fast DMA price associated with this crossover
public double CurrentPrice {get{return SlowDMAPrice.CurrentPrice;}} // it doesn't matter if sourced from slow or fast since they both refer to the same underlying price
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append("Symbol:").Append(Symbol);
sb.Append(" Date:").Append(EventDate.ToShortDateString());
sb.Append(" Direction:").Append(CrossOverDirection.ToString());
sb.Append(" IsChangeInTrend:").Append(IsChangeInTrend.ToString());
sb.Append(" ChangePercent:").Append(Utility.FormatNumber(ChangePercent,2));
sb.Append(" ClosePrice:").Append(Utility.FormatCurrency(CurrentPrice));
return sb.ToString();
}
}
}

View File

@@ -0,0 +1,255 @@
using System;
using System.Collections.Generic;
using MarketData.MarketDataModel;
using MarketData.DataAccess;
using MarketData.Numerical;
using MarketData.Utils;
namespace MarketData.Generator.MovingAverage
{
public class MovingAverageGenerator
{
public static readonly int DayCount200=200;
public static readonly int DayCount100=100;
public static readonly int DayCount55=55;
public static readonly int DayCount50=50;
public static readonly int DayCount21=21;
public static readonly int DayCount5=5;
private MovingAverageGenerator()
{
}
public static MovingAverages GenerateMovingAverages(String symbol,int dayCount=180)
{
try
{
Dictionary<DateTime, DMAPrice> ma200ByDate = new Dictionary<DateTime, DMAPrice>();
Dictionary<DateTime, DMAPrice> ma100ByDate = new Dictionary<DateTime, DMAPrice>();
Dictionary<DateTime, DMAPrice> ma55ByDate = new Dictionary<DateTime, DMAPrice>();
Dictionary<DateTime, DMAPrice> ma21ByDate = new Dictionary<DateTime, DMAPrice>();
Dictionary<DateTime, DMAPrice> ma5ByDate = new Dictionary<DateTime, DMAPrice>();
if (null == symbol) return null;
String companyName = PricingDA.GetNameForSymbol(symbol);
DateGenerator dateGenerator = new DateGenerator();
DateTime startDate = dateGenerator.GetPrevBusinessDay(DateTime.Now);
Prices prices = PricingDA.GetPrices(symbol, startDate, dayCount);
if (null == prices || 0 == prices.Count)
{
MDTrace.WriteLine(LogLevel.DEBUG,"No prices for symbol '" + symbol + "'");
return null;
}
Price latestPrice = prices[0];
DMAPrices ma200= GenerateMovingAverage(prices, DayCount200);
DMAPrices ma100 = GenerateMovingAverage(prices, DayCount100);
DMAPrices ma55 = GenerateMovingAverage(prices, DayCount55);
DMAPrices ma21 = GenerateMovingAverage(prices, DayCount21);
DMAPrices ma5 = GenerateMovingAverage(prices, DayCount5);
for (int index = 0; index < ma200.Count; index++) ma200ByDate.Add(ma200[index].Date,ma200[index]);
for (int index = 0; index < ma100.Count; index++) ma100ByDate.Add(ma100[index].Date, ma100[index]);
for (int index = 0; index < ma55.Count; index++) ma55ByDate.Add(ma55[index].Date, ma55[index]);
for (int index = 0; index < ma21.Count; index++) ma21ByDate.Add(ma21[index].Date, ma21[index]);
for (int index = 0; index < ma5.Count; index++) ma5ByDate.Add(ma5[index].Date, ma5[index]);
MovingAverages movingAverages = new MovingAverages();
movingAverages.ThruDate = prices[0].Date;
movingAverages.FromDate = prices[prices.Count - 1].Date;
for (int index = 0; index < prices.Count; index++)
{
Price price = prices[index];
DMAPrice ma200Price = null;
DMAPrice ma100Price = null;
DMAPrice ma55Price = null;
DMAPrice ma21Price = null;
DMAPrice ma5Price = null;
if (ma55ByDate.ContainsKey(price.Date)) ma55Price=ma55ByDate[price.Date];
if (ma21ByDate.ContainsKey(price.Date)) ma21Price = ma21ByDate[price.Date];
if (ma5ByDate.ContainsKey(price.Date)) ma5Price = ma5ByDate[price.Date];
if(ma200ByDate.ContainsKey(price.Date))ma200Price = ma200ByDate[price.Date];
if(ma100ByDate.ContainsKey(price.Date)) ma100Price = ma100ByDate[price.Date];
MovingAverageElement movingAverageElement = new MovingAverageElement();
movingAverageElement.Symbol = price.Symbol;
movingAverageElement.Date = price.Date;
movingAverageElement.Close = price.Close;
movingAverageElement.High = price.High;
movingAverageElement.Low = price.Low;
movingAverageElement.MA200 = null==ma200Price?double.NaN:ma200Price.AVGPrice;
movingAverageElement.MA100 = null == ma100Price ? double.NaN : ma100Price.AVGPrice;
movingAverageElement.MA55 = null==ma55Price?double.NaN:ma55Price.AVGPrice;
movingAverageElement.MA21 = null==ma21Price?double.NaN:ma21Price.AVGPrice;
movingAverageElement.MA5 = null==ma5Price?double.NaN:ma5Price.AVGPrice;
movingAverages.Add(movingAverageElement);
}
return movingAverages;
}
catch (Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception);
return null;
}
}
public static DMAValues GenerateMovingAverage(DMAValues values,int dayCount)
{
try
{
DMAValues dmaValues = new DMAValues();
for (int index = 0; index < values.Count; index++)
{
DMAValue value = values[index];
DMAValue dmaValue = new DMAValue();
dmaValue.Date = value.Date;
float[] pricesArray = values.GetValues(index, dayCount);
if (null == pricesArray) break;
dmaValue.MAValue = Numerics.Mean(ref pricesArray);
dmaValue.Value = value.Value;
if (double.IsNaN(dmaValue.MAValue)) continue;
dmaValues.Add(dmaValue);
}
return dmaValues;
}
catch (Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception);
return null;
}
}
/// <summary>
/// The EMA coverage shoudld be the same as the simple moving average. If you have 10 elements in the SMA then you should have 10 elements in the EMA
/// The EMA smooths the line by applying a smoothing (Beta) where Beta=1/(dayCount+1). Foe example if you are wanting to calculate the
/// 20 day exponential moving average over a series then Beta=2/(20+1)=.095
/// The formula: EMA=prevEMA.AVGPrice+beta*(smaPrice.CurrentPrice - prevEMA.AVGPrice)
/// Tom Basso uses a 9 day(Fast) and 41 day(Slow) exponential moving average crossover to determine change in trend direction.
/// </summary>
/// <param name="prices"></param>
/// <param name="dayCount"></param>
/// <returns></returns>
public static DMAPrices GenerateExponentialMovingAverage(Prices prices,int dayCount)
{
try
{
if(null==prices||prices.Count<dayCount+1)return null; // (i.e.) a 20 day ExponentialMovingAverage requires 21 days of data
double beta=2.00/((double)dayCount+1.00);
DMAPrices emaPrices=new DMAPrices();
DMAPrices smaPrices=GenerateMovingAverage(prices,dayCount);
if(null==smaPrices||smaPrices.Count<dayCount)return null;
for(int index=0;index<smaPrices.Count;index++)
{
DMAPrice smaPrice=smaPrices[index];
DMAPrice emaPrice=new DMAPrice();
if(0==index)
{
emaPrice.Symbol=smaPrice.Symbol;
emaPrice.Date=smaPrice.Date;
emaPrice.AVGPrice=smaPrice.AVGPrice;
emaPrice.CurrentPrice=smaPrice.CurrentPrice;
emaPrice.MaxPrice=smaPrice.MaxPrice;
emaPrice.MinPrice=smaPrice.MinPrice;
emaPrices.Add(emaPrice);
}
else
{
DMAPrice prevEMA=emaPrices[emaPrices.Count-1];
emaPrice.Symbol=smaPrice.Symbol;
emaPrice.Date=smaPrice.Date;
emaPrice.CurrentPrice=smaPrice.CurrentPrice;
emaPrice.MaxPrice=smaPrice.MaxPrice;
emaPrice.MinPrice=smaPrice.MinPrice;
emaPrice.AVGPrice = prevEMA.AVGPrice+beta*(smaPrice.CurrentPrice - prevEMA.AVGPrice);
emaPrices.Add(emaPrice);
}
}
return emaPrices;
}
catch (Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception);
return null;
}
}
/// <summary>Generates a dayCount moving average given prices.</summary>
public static DMAPrices GenerateMovingAverage(Prices prices, int dayCount)
{
try
{
DMAPrices dmaPrices = new DMAPrices();
for (int index = 0; index < prices.Count; index++)
{
Price price = prices[index];
DMAPrice dmaPrice = new DMAPrice();
dmaPrice.Symbol = price.Symbol;
dmaPrice.Date = price.Date;
float[] pricesArray = prices.GetPrices(index, dayCount);
if (null == pricesArray) break;
dmaPrice.AVGPrice = Numerics.Mean(ref pricesArray);
if (double.IsNaN(dmaPrice.AVGPrice)) continue;
dmaPrice.CurrentPrice = price.Close;
dmaPrices.Add(dmaPrice);
}
return dmaPrices;
}
catch (Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception);
return null;
}
}
public static DMAPrices GenerateMovingMinsOnLow(Prices prices, int dayCount)
{
try
{
DMAPrices dmaPrices = new DMAPrices();
for (int index = 0; index < prices.Count; index++)
{
Price price = prices[index];
DMAPrice dmaPrice = new DMAPrice();
dmaPrice.Symbol = price.Symbol;
dmaPrice.Date = price.Date;
float[] pricesArray = prices.GetPricesLow(index, dayCount);
if (null == pricesArray) break;
dmaPrice.MinPrice = Numerics.Min(ref pricesArray);
if (double.IsNaN(dmaPrice.MinPrice)) continue;
dmaPrice.CurrentPrice = price.Low;
dmaPrices.Add(dmaPrice);
}
return dmaPrices;
}
catch (Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
return null;
}
}
public static DMAPrices GenerateMovingMaxOnHigh(Prices prices, int dayCount)
{
try
{
DMAPrices dmaPrices = new DMAPrices();
for (int index = 0; index < prices.Count; index++)
{
Price price = prices[index];
DMAPrice dmaPrice = new DMAPrice();
dmaPrice.Symbol = price.Symbol;
dmaPrice.Date = price.Date;
float[] pricesArray = prices.GetPricesHigh(index, dayCount);
if (null == pricesArray) break;
dmaPrice.MaxPrice = Numerics.Max(ref pricesArray);
if (double.IsNaN(dmaPrice.MaxPrice)) continue;
dmaPrice.CurrentPrice = price.High;
dmaPrices.Add(dmaPrice);
}
return dmaPrices;
}
catch (Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
return null;
}
}
}
}

View File

@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MarketData.Generator.GainLoss;
using MarketData.DataAccess;
using MarketData.MarketDataModel;
using MarketData.Utils;
namespace MarketData.Generator
{
public class ParityGenerator
{
private enum Direction { Negative, Positive, None };
private ParityGenerator()
{
}
public static ParityElement GenerateBreakEven(String symbol)
{
try
{
ParityElement parityElement=new ParityElement();
Price zeroPrice=null;
PortfolioTrades portfolioTrades = PortfolioDA.GetTradesSymbol(symbol);
if(null==portfolioTrades||0==portfolioTrades.Count)return null;
PortfolioTrades openTrades=portfolioTrades.GetOpenTrades();
DateTime pricingDate = PricingDA.GetLatestDate(symbol);
Price latestPrice = PricingDA.GetPrice(symbol, pricingDate);
GainLossGenerator gainLossGenerator=new GainLossGenerator();
zeroPrice=ParityGenerator.GenerateGainLossValue(openTrades,latestPrice);
parityElement.ParityOffsetPrice=zeroPrice.Close;
parityElement.ParityOffsetPercent=((latestPrice.Close-zeroPrice.Close)/zeroPrice.Close);
parityElement.Symbol=symbol;
parityElement.PricingDate=pricingDate;
return parityElement;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0}",exception));
return null;
}
}
// generate gain/loss value for portfolio of like symbols
// This funtion will return the dollar price that the stock must either rise to (when G/L is negative) or fall to (when the G/L is positive)
// in order to produce a zero return (i.e.) a wash.
public static Price GenerateGainLossValue(PortfolioTrades portfolioTrades,Price givenPrice)
{
double? lowerClamp=null;
double? upperClamp=null;
double? gainLoss=null;
double? prevGuess=null;
Direction direction=Direction.None;
int iterations=0;
if(null==portfolioTrades||0==portfolioTrades.Count) return null;
Price price=givenPrice.Clone();
gainLoss=portfolioTrades.GetGainLoss(price);
if(gainLoss<0) direction=Direction.Negative;
else direction=Direction.Positive;
while(true)
{
if(IsZero(gainLoss)) break;
if(null==lowerClamp&&null==upperClamp&&gainLoss<0&&direction.Equals(Direction.Negative))
{
prevGuess=price.Close;
price.Close=price.Close+(price.Close/2.00);
iterations++;
}
else if(null==lowerClamp&&null==upperClamp&&gainLoss>=0&&direction.Equals(Direction.Positive))
{
prevGuess=price.Close;
price.Close=price.Close-(price.Close/2.00);
iterations++;
}
else
{
if(gainLoss<0)
{
lowerClamp=price.Close;
if(null==upperClamp) upperClamp=prevGuess;
price.Close=(lowerClamp.Value+upperClamp.Value)/2.00;
prevGuess=price.Close;
iterations++;
}
else
{
upperClamp=price.Close;
if(null==lowerClamp) lowerClamp=prevGuess;
price.Close=(upperClamp.Value+lowerClamp.Value)/2.00;
prevGuess=price.Close;
iterations++;
}
}
gainLoss=portfolioTrades.GetGainLoss(price);
}
return price;
}
public static bool IsZero(double? value)
{
if(null==value)
{
return true;
}
int intValue=(int)(value*10000.00);
if(0==intValue) return true;
return false;
}
}
}

View File

@@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using MarketData.MarketDataModel;
using MarketData.Utils;
using MarketData.DataAccess;
namespace MarketData.Generator
{
public class RSIGenerator
{
private RSIGenerator()
{
}
public static RSICollection GenerateRSI(String symbol,int priceCount,int rsiDayCount=14)
{
if(priceCount<rsiDayCount)priceCount=(rsiDayCount*2)+1;
Prices prices=PricingDA.GetPrices(symbol,priceCount);
if(null==prices||priceCount!=prices.Count)return null;
RSICollection rsiCollection=new RSICollection();
for(int index=prices.Count-1;index>=0;index--)
{
Price price=prices[index];
RSIElement rsiElement=new RSIElement();
rsiElement.Symbol=price.Symbol;
rsiElement.RSIDays=rsiDayCount;
rsiElement.Date=price.Date;
rsiElement.Close=price.Close;
if(index==prices.Count-1)continue;
rsiElement.Change=price.Close-prices[index+1].Close;
if(rsiElement.Change<0){rsiElement.Loss=Math.Abs(rsiElement.Change);rsiElement.Gain=0.00;}
else if(rsiElement.Change>0){rsiElement.Gain=rsiElement.Change;rsiElement.Loss=0.00;}
else{rsiElement.Loss=0.00;rsiElement.Gain=0.00;}
rsiCollection.Add(rsiElement);
}
RSICollection topCollection=rsiCollection.Top(rsiDayCount,1);
rsiCollection[rsiDayCount].AverageGain=topCollection.AverageGain();
rsiCollection[rsiDayCount].AverageLoss=topCollection.AverageLoss();
if(0.00==rsiCollection[rsiDayCount].AverageLoss)
{
rsiCollection[rsiDayCount].RS=0.00;
rsiCollection[rsiDayCount].RSI=100.00;
}
else
{
rsiCollection[rsiDayCount].RS=rsiCollection[rsiDayCount].AverageGain/rsiCollection[rsiDayCount].AverageLoss;
rsiCollection[rsiDayCount].RSI=100.00-(100.00/(1.00+rsiCollection[rsiDayCount].RS));
}
for(int index=rsiDayCount+1;index<rsiCollection.Count;index++)
{
rsiCollection[index].AverageGain=(rsiCollection[index-1].AverageGain*(rsiDayCount-1)+rsiCollection[index].Gain)/(double)rsiDayCount;
rsiCollection[index].AverageLoss=(rsiCollection[index-1].AverageLoss*(rsiDayCount-1)+rsiCollection[index].Loss)/(double)rsiDayCount;
if(0.00==rsiCollection[index].AverageLoss)
{
rsiCollection[index].RS=0.00;
rsiCollection[index].RSI=100.00;
}
else
{
rsiCollection[index].RS=rsiCollection[index].AverageGain/rsiCollection[index].AverageLoss;
rsiCollection[index].RSI=100.00-(100.00/(1.00+rsiCollection[index].RS));
}
}
rsiCollection=new RSICollection(rsiCollection.Skip(rsiDayCount).ToList());
return rsiCollection;
// return rsiCollection;
}
public static RSICollection GenerateRSI(String symbol,DateTime analysisDate,int priceCount,int rsiDayCount=14)
{
if(priceCount<rsiDayCount)priceCount=(rsiDayCount*2)+1;
Prices prices=PricingDA.GetPrices(symbol,analysisDate,priceCount);
if(null==prices||priceCount!=prices.Count)return null;
RSICollection rsiCollection=new RSICollection();
for(int index=prices.Count-1;index>=0;index--)
{
Price price=prices[index];
RSIElement rsiElement=new RSIElement();
rsiElement.Symbol=price.Symbol;
rsiElement.RSIDays=rsiDayCount;
rsiElement.Date=price.Date;
rsiElement.Close=price.Close;
if(index==prices.Count-1)continue;
rsiElement.Change=price.Close-prices[index+1].Close;
if(rsiElement.Change<0){rsiElement.Loss=Math.Abs(rsiElement.Change);rsiElement.Gain=0.00;}
else if(rsiElement.Change>0){rsiElement.Gain=rsiElement.Change;rsiElement.Loss=0.00;}
else{rsiElement.Loss=0.00;rsiElement.Gain=0.00;}
rsiCollection.Add(rsiElement);
}
RSICollection topCollection=rsiCollection.Top(rsiDayCount,1);
rsiCollection[rsiDayCount].AverageGain=topCollection.AverageGain();
rsiCollection[rsiDayCount].AverageLoss=topCollection.AverageLoss();
if(0.00==rsiCollection[rsiDayCount].AverageLoss)
{
rsiCollection[rsiDayCount].RS=0.00;
rsiCollection[rsiDayCount].RSI=100.00;
}
else
{
rsiCollection[rsiDayCount].RS=rsiCollection[rsiDayCount].AverageGain/rsiCollection[rsiDayCount].AverageLoss;
rsiCollection[rsiDayCount].RSI=100.00-(100.00/(1.00+rsiCollection[rsiDayCount].RS));
}
for(int index=rsiDayCount+1;index<rsiCollection.Count;index++)
{
rsiCollection[index].AverageGain=(rsiCollection[index-1].AverageGain*(rsiDayCount-1)+rsiCollection[index].Gain)/(double)rsiDayCount;
rsiCollection[index].AverageLoss=(rsiCollection[index-1].AverageLoss*(rsiDayCount-1)+rsiCollection[index].Loss)/(double)rsiDayCount;
if(0.00==rsiCollection[index].AverageLoss)
{
rsiCollection[index].RS=0.00;
rsiCollection[index].RSI=100.00;
}
else
{
rsiCollection[index].RS=rsiCollection[index].AverageGain/rsiCollection[index].AverageLoss;
rsiCollection[index].RSI=100.00-(100.00/(1.00+rsiCollection[index].RS));
}
}
rsiCollection=new RSICollection(rsiCollection.Skip(rsiDayCount).ToList());
return rsiCollection;
// return rsiCollection;
}
}
}

View File

@@ -0,0 +1,90 @@
using MarketData.DataAccess;
using MarketData.MarketDataModel;
using MarketData.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MarketData.Generator
{
public class RiskFreeRateGenerator
{
private RiskFreeRateGenerator()
{
}
public static Price GeneratePriceAtRiskFreeRate(PortfolioTrades portfolioTrades)
{
Price price=new Price();
double cumulativePortfolioInvestment=0;
if(null==portfolioTrades||0==portfolioTrades.Count) return null;
double totalShares=(from PortfolioTrade portfolioTrade in portfolioTrades select portfolioTrade.Shares).Sum();
List<int> yearsToProcess=GetYearsToProcess(portfolioTrades);
foreach(int processYear in yearsToProcess)
{
cumulativePortfolioInvestment=GetInvestmentForYear(portfolioTrades,processYear,cumulativePortfolioInvestment);
if(double.NaN.Equals(cumulativePortfolioInvestment)) return null;
}
price.Close=price.Open=price.High=price.Low=price.AdjClose=cumulativePortfolioInvestment/totalShares;
price.Date=DateTime.Now;
return price;
}
private static List<int> GetYearsToProcess(PortfolioTrades portfolioTrades)
{
List<int> investmentYears=new List<int>();
int startingYear=portfolioTrades[0].TradeDate.Year;
int endingYear=DateTime.Now.Year;
for(int processYear=startingYear;processYear<=endingYear;processYear++)
{
investmentYears.Add(processYear);
}
return investmentYears;
}
private static double GetInvestmentForYear(PortfolioTrades portfolioTrades,int investmentYear,double initialInvestment)
{
double cumulativeYearInvestment=0;
DateTime yearEnd=new DateTime(investmentYear,12,31);
DateTime startOfYear=new DateTime(investmentYear,1,1);
double riskFreeRate;
PortfolioTrades tradesInYear=new PortfolioTrades((from PortfolioTrade portfolioTrade in portfolioTrades where portfolioTrade.TradeDate.Year.Equals(investmentYear) select portfolioTrade).ToList());
if(0.00!=initialInvestment)
{
riskFreeRate=YieldCurveDA.GetRiskFreeRate1Yr(startOfYear,10);
if(double.NaN.Equals(riskFreeRate)) return double.NaN;
riskFreeRate/=100.00;
double fractionOfYearHeld=1.00;
if(yearEnd>DateTime.Now)
{
fractionOfYearHeld=GetFractionOfYearHeld(DateTime.Now,startOfYear);
cumulativeYearInvestment=initialInvestment*Math.Pow(1.00+riskFreeRate,fractionOfYearHeld);
}
else cumulativeYearInvestment=initialInvestment*Math.Pow(1.00+riskFreeRate,fractionOfYearHeld);
}
foreach(PortfolioTrade portfolioTrade in tradesInYear)
{
riskFreeRate=YieldCurveDA.GetRiskFreeRate1Yr(portfolioTrade.TradeDate,10); // suggestion: move this database call out of the inner loop
if(double.NaN.Equals(riskFreeRate)) return double.NaN;
riskFreeRate/=100.00;
double fractionOfYearHeld=GetFractionOfYearHeld(portfolioTrade,yearEnd);
double tradeInvestment=portfolioTrade.Exposure()*Math.Pow((1.00+riskFreeRate),fractionOfYearHeld);
cumulativeYearInvestment+=tradeInvestment;
}
return cumulativeYearInvestment;
}
private static double GetFractionOfYearHeld(PortfolioTrade portfolioTrade,DateTime yearEnd)
{
DateGenerator dateGenerator=new DateGenerator();
double fractionOfYearHeld=dateGenerator.DaysBetween(portfolioTrade.TradeDate,yearEnd)/365.00;
return fractionOfYearHeld;
}
private static double GetFractionOfYearHeld(DateTime effectiveDate,DateTime yearEnd)
{
DateGenerator dateGenerator=new DateGenerator();
double fractionOfYearHeld=dateGenerator.DaysBetween(effectiveDate,yearEnd)/365.00;
return fractionOfYearHeld;
}
}
}

View File

@@ -0,0 +1,417 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using MarketData.DataAccess;
using MarketData.Utils;
using MarketData.MarketDataModel;
namespace MarketData.Generator
{
public class SentimentGenerator
{
private LexicalDictionary negativeLexicon=null;
private LexicalDictionary positiveLexicon=null;
private WordDictionary wordDictionary=null;
private static SentimentGenerator sentimentGeneratorInstance=null;
private SentimentGenerator()
{
// InitializeLexicalDatabase(); // Initialize the lexical sentiment database
// UpdatePartsOfSpeech(); // and update the parts of speech
// InitializeWordDatabase(); // Initialize the master word dictionary
negativeLexicon=LexicalDA.GetLexicalCollection().ToDictionary(LexicalElement.NEGATIVE_SENTIMENT);
positiveLexicon=LexicalDA.GetLexicalCollection().ToDictionary(LexicalElement.POSITIVE_SENTIMENT);
wordDictionary=DictionaryDA.GetDictionaryCollection().ToDictionary();
}
public static SentimentGenerator GetInstance()
{
lock (typeof(SentimentGenerator))
{
if (null == sentimentGeneratorInstance) sentimentGeneratorInstance = new SentimentGenerator();
return sentimentGeneratorInstance;
}
}
// ********************************************************************************************************************************************************************************************
// ******************************************************************************* B O O T S T R A P P I N G **********************************************************************************
// ********************************************************************************************************************************************************************************************
private static void InitializeWordDatabase()
{
List<String> verbs=LoadFile(@"C:\boneyard\marketdata\LexicalAnalysis\verbs.txt").Values.ToList<String>();
List<String> adjectives=LoadFile(@"C:\boneyard\marketdata\LexicalAnalysis\adjectives.txt").Values.ToList<String>();
List<String> adverbs=LoadFile(@"C:\boneyard\marketdata\LexicalAnalysis\adverbs.txt").Values.ToList<String>();
List<String> nouns=LoadFile(@"C:\boneyard\marketdata\LexicalAnalysis\nouns.txt").Values.ToList<String>();
DictionaryDA.TruncateDictionary();
DictionaryCollection collection=new DictionaryCollection();
foreach(String word in verbs)collection.Add(new DictionaryElement(word,LexicalElement.VERB));
DictionaryDA.InsertDictionaryCollection(collection);
collection=new DictionaryCollection();
foreach(String word in adjectives)collection.Add(new DictionaryElement(word,LexicalElement.ADJECTIVE));
DictionaryDA.InsertDictionaryCollection(collection);
collection=new DictionaryCollection();
foreach(String word in adverbs)collection.Add(new DictionaryElement(word,LexicalElement.ADVERB));
DictionaryDA.InsertDictionaryCollection(collection);
collection=new DictionaryCollection();
foreach(String word in nouns)collection.Add(new DictionaryElement(word,LexicalElement.NOUN));
DictionaryDA.InsertDictionaryCollection(collection);
}
private static void InitializeLexicalDatabase()
{
Dictionary<String,String> negativeLexicon=LoadFile(@"C:\boneyard\marketdata\LexicalAnalysis\negativelexicon.txt");
Dictionary<String,String> positiveLexicon=LoadFile(@"C:\boneyard\marketdata\LexicalAnalysis\positivelexicon.txt");
List<String> negativeItems=new List<String>(negativeLexicon.Values);
List<String> positiveItems=new List<String>(positiveLexicon.Values);
LexicalCollection positiveLexicalElements=new LexicalCollection();
LexicalCollection negativeLexicalElements=new LexicalCollection();
foreach(String s in negativeItems)negativeLexicalElements.Add(new LexicalElement(s,LexicalElement.NEGATIVE_SENTIMENT));
foreach(String s in positiveItems)positiveLexicalElements.Add(new LexicalElement(s,LexicalElement.POSITIVE_SENTIMENT));
LexicalDA.TruncateLexicon();
LexicalDA.InsertLexicalCollection(negativeLexicalElements);
LexicalDA.InsertLexicalCollection(positiveLexicalElements);
}
private static void UpdatePartsOfSpeech()
{
List<String> verbs=LoadFile(@"C:\boneyard\marketdata\LexicalAnalysis\verbs.txt").Values.ToList<String>();
List<String> adjectives=LoadFile(@"C:\boneyard\marketdata\LexicalAnalysis\adjectives.txt").Values.ToList<String>();
List<String> adverbs=LoadFile(@"C:\boneyard\marketdata\LexicalAnalysis\adverbs.txt").Values.ToList<String>();
List<String> nouns=LoadFile(@"C:\boneyard\marketdata\LexicalAnalysis\nouns.txt").Values.ToList<String>();
LexicalDictionary negativeLexicon=LexicalDA.GetLexicalCollection().ToDictionary(LexicalElement.NEGATIVE_SENTIMENT);
LexicalDictionary positiveLexicon=LexicalDA.GetLexicalCollection().ToDictionary(LexicalElement.POSITIVE_SENTIMENT);
ApplyList(verbs,negativeLexicon,positiveLexicon,LexicalElement.VERB);
ApplyList(adjectives,negativeLexicon,positiveLexicon,LexicalElement.ADJECTIVE);
ApplyList(adverbs,negativeLexicon,positiveLexicon,LexicalElement.ADVERB);
ApplyList(nouns,negativeLexicon,positiveLexicon,LexicalElement.NOUN);
LexicalDA.UpdateLexicalElements(negativeLexicon.ToList());
LexicalDA.UpdateLexicalElements(positiveLexicon.ToList());
}
private static void ApplyList(List<String> list,Dictionary<String,LexicalElement> negativeItems,Dictionary<String,LexicalElement> positiveItems,String partOfSpeech)
{
foreach(String item in list)
{
if(negativeItems.ContainsKey(item))
{
LexicalElement lexicalElement=negativeItems[item];
if(null==lexicalElement.PartOfSpeech)lexicalElement.PartOfSpeech=partOfSpeech;
}
if(positiveItems.ContainsKey(item))
{
LexicalElement lexicalElement=positiveItems[item];
if(null==lexicalElement.PartOfSpeech)lexicalElement.PartOfSpeech=partOfSpeech;
}
}
}
private static Dictionary<String,String> LoadFile(String pathFileName)
{
StreamReader streamReader=null;
Dictionary<String,String> items=new Dictionary<String,String>();
try
{
streamReader = File.OpenText(pathFileName);
String strLine = null;
while (null != (strLine = streamReader.ReadLine()))
{
if ("".Equals(strLine))continue;
if(items.ContainsKey(strLine))continue;
items.Add(strLine,strLine);
}
return items;
}
catch(Exception /*exception*/)
{
return null;
}
finally
{
if(null!=streamReader)
{
streamReader.Close();
streamReader.Dispose();
}
}
}
// ******************************************************************************************************************************************************************************************
// ******************************************************************************************************************************************************************************************
// ******************************************************************************************************************************************************************************************
public HeadlineSentiment ProcessHeadline(Headline headline)
{
lock(this)
{
String sentiment=ProcessHeadlineEntry(headline.Entry);
return new HeadlineSentiment(headline,sentiment);
}
}
public String ProcessHeadlineEntry(String headline)
{
lock(this)
{
StringBuilder sb=new StringBuilder();
Dictionary<String,String> uniqueWords=new Dictionary<String,String>();
// String[] words=headline.Split(' ');
String[] words=headline.Split(new char[]{' ','-'});
double positionRank=1;
double positiveRank=0.00;
double negativeRank=0.00;
for(int index=words.Length-1;index>=0;index--)
{
String word=words[index];
String cleanWord=word.Trim().ToLower();
cleanWord=CleanASCII(cleanWord);
if(!Keep(cleanWord))continue;
cleanWord=Clean(cleanWord);
cleanWord=CleanStartsWithEndsWith(cleanWord);
if(!KeepStartsWithEndsWith(cleanWord))continue;
if(IsNumber(word))continue;
cleanWord=cleanWord.Trim();
if(0==cleanWord.Length)continue;
bool isNegative=IsNegativeWord(cleanWord);
bool isPositive=IsPositiveWord(cleanWord);
bool isVerb=IsPartOfSpeech(cleanWord,LexicalElement.VERB);
bool isNoun=IsPartOfSpeech(cleanWord,LexicalElement.NOUN);
bool isAdjective=IsPartOfSpeech(cleanWord,LexicalElement.ADJECTIVE);
// Console.WriteLine(String.Format("Word '{0}':{1}:{2}:Pr{3}",cleanWord,LexicalElement.VERB,isPositive?"Positive":isNegative?"Negative":"Neutral",positionRank));
if(isNegative)
{
negativeRank+=positionRank;
if(isAdjective&&index==0) // it's describing the next word so don't adjust anything
{
negativeRank-=positionRank;
}
else if(isVerb&&index>0) // if the word is a verb check to see if previous word is an adjective thus strengthening it
{
String preceedingWord=words[index-1].Trim().ToLower();
if(IsPartOfSpeech(preceedingWord,LexicalElement.ADJECTIVE))
{
// Console.WriteLine(String.Format("Preceeding Word {0}:{1}",preceedingWord,LexicalElement.ADJECTIVE));
negativeRank+=(--positionRank);
index--;
continue;
}
else if(isNoun) // if the word is also a noun and we have positive verb following the we are doing negative things to something positive
{
List<String> nextNouns = FindNextNouns(words, index + 1);
for (int nounIndex = 0; nounIndex < nextNouns.Count; nounIndex++)
{
isNegative = IsNegativeWord(nextNouns[nounIndex]);
isPositive = IsPositiveWord(nextNouns[nounIndex]);
if (isPositive) negativeRank++;
}
}
}
else if(isNoun&&index>0)
{
List<String> nextVerbs=FindNextVerbs(words,index+1); // if we have a negative noun then look for a following positive verb that would strengthen the negativity
for(int verbIndex=0;verbIndex<nextVerbs.Count;verbIndex++)
{
isNegative = IsNegativeWord(nextVerbs[verbIndex]);
isPositive = IsPositiveWord(nextVerbs[verbIndex]);
if(isPositive)negativeRank++;
}
continue;
}
}
else if(isPositive)
{
positiveRank+=positionRank;
if (isAdjective && index == 0) // it's describing the next word so don't adjust anything
{
positiveRank -= positionRank;
}
else if(isVerb&&index>0) // if the word is a verb check to see if previous word is an adjective thus strengthening it
{
String preceedingWord=words[index-1].Trim().ToLower();
if(IsPartOfSpeech(preceedingWord,LexicalElement.ADJECTIVE))
{
// Console.WriteLine(String.Format("Preceeding Word {0}:{1}",preceedingWord,LexicalElement.ADJECTIVE));
positiveRank+=(--positionRank);
index--;
continue;
}
}
}
if(uniqueWords.ContainsKey(cleanWord))continue;
uniqueWords.Add(cleanWord,cleanWord);
positionRank++;
} // for each word in headline
String headlineSentiment=LexicalElement.NEUTRAL_SENTIMENT;
if(positiveRank>negativeRank)headlineSentiment=LexicalElement.POSITIVE_SENTIMENT;
else if(negativeRank>positiveRank)headlineSentiment=LexicalElement.NEGATIVE_SENTIMENT;
return headlineSentiment;
}
}
private List<String> FindNextVerbs(String[] words, int startingIndex)
{
List<String> verbs=new List<String>();
for (int index = startingIndex; index < words.Length; index++)
{
if(IsPartOfSpeech(words[index],LexicalElement.VERB))verbs.Add(words[index]);
}
return verbs;
}
private List<String> FindNextNouns(String[] words, int startingIndex)
{
List<String> verbs = new List<String>();
for (int index = startingIndex; index < words.Length; index++)
{
if (IsPartOfSpeech(words[index], LexicalElement.NOUN)) verbs.Add(words[index]);
}
return verbs;
}
public void PrintHeadlineEntry(String headline)
{
lock (this)
{
StringBuilder sb = new StringBuilder();
Dictionary<String, String> uniqueWords = new Dictionary<String, String>();
String[] words = headline.Split(new char[] { ' ', '-' });
for (int index = words.Length - 1; index >= 0; index--)
{
String word = words[index];
String cleanWord = word.Trim().ToLower();
cleanWord = CleanASCII(cleanWord);
if (!Keep(cleanWord)) continue;
cleanWord = Clean(cleanWord);
cleanWord = CleanStartsWithEndsWith(cleanWord);
if (!KeepStartsWithEndsWith(cleanWord)) continue;
if (IsNumber(word)) continue;
cleanWord = cleanWord.Trim();
if (0 == cleanWord.Length) continue;
bool isNegative = IsNegativeWord(cleanWord);
bool isPositive = IsPositiveWord(cleanWord);
bool isVerb = IsPartOfSpeech(cleanWord, LexicalElement.VERB);
bool isNoun = IsPartOfSpeech(cleanWord, LexicalElement.NOUN);
String partOfSpeech = null;
if (isNegative || isPositive) partOfSpeech = GetPartOfSpeech(cleanWord);
if (null == partOfSpeech) partOfSpeech = GetDictionaryPartOfSpeech(word);
if (null == partOfSpeech) partOfSpeech = "?";
Console.WriteLine(String.Format("Word '{0}':{1}:{2}", cleanWord, partOfSpeech, isPositive ? "Positive" : isNegative ? "Negative" : "Neutral"));
if (uniqueWords.ContainsKey(cleanWord)) continue;
uniqueWords.Add(cleanWord, cleanWord);
} // for each word in headline
}
}
public HeadlinesSentiment ProcessHeadlines(Headlines headlines)
{
lock(this)
{
HeadlinesSentiment headlineSentimentList=new HeadlinesSentiment();
foreach(Headline headline in headlines)
{
HeadlineSentiment headlineSentiment=ProcessHeadline(headline);
headlineSentimentList.Add(headlineSentiment);
} // for each headline
return headlineSentimentList;
}
}
private bool IsNegativeWord(String word)
{
if(negativeLexicon.ContainsKey(word))return true;
return false;
}
private bool IsPositiveWord(String word)
{
if(positiveLexicon.ContainsKey(word))return true;
return false;
}
private bool IsPartOfSpeech(String word,String partOfSpeech)
{
if(null==word||null==partOfSpeech)return false;
if (positiveLexicon.ContainsKey(word) && partOfSpeech.Equals(positiveLexicon[word].PartOfSpeech, StringComparison.OrdinalIgnoreCase)) return true;
if (negativeLexicon.ContainsKey(word) && partOfSpeech.Equals(negativeLexicon[word].PartOfSpeech, StringComparison.OrdinalIgnoreCase)) return true;
if(wordDictionary.ContainsKeyAs(word,partOfSpeech))return true;
return false;
}
private String GetPartOfSpeech(String word)
{
if(positiveLexicon.ContainsKey(word))return positiveLexicon[word].PartOfSpeech;
else if(negativeLexicon.ContainsKey(word))return negativeLexicon[word].PartOfSpeech;
return null;
}
private String GetDictionaryPartOfSpeech(String word)
{
if(!wordDictionary.ContainsKey(word))return null;
if(wordDictionary.ContainsKeyAs(word,LexicalElement.ADJECTIVE))return LexicalElement.ADJECTIVE;
if(wordDictionary.ContainsKeyAs(word,LexicalElement.ADVERB))return LexicalElement.ADVERB;
if(wordDictionary.ContainsKeyAs(word,LexicalElement.NOUN))return LexicalElement.NOUN;
if(wordDictionary.ContainsKeyAs(word,LexicalElement.VERB))return LexicalElement.VERB;
if(wordDictionary.ContainsKeyAs(word,LexicalElement.CONJUNCTION))return LexicalElement.CONJUNCTION;
return null;
}
private bool Keep(String word)
{
// String[] removeList={"/","%",",",":","-",";","$",">","+","?","_","#","&"};
// String[] removeList={"/","%","-",";","$",">","+","?","_","#","&"};
String[] removeList={"/","%",";","$",">","+","?","_","#","&"};
foreach(String match in removeList)
{
if(word.Contains(match))return false;
}
return true;
}
private string CleanASCII(string s)
{
StringBuilder sb = new StringBuilder(s.Length);
foreach(char c in s)
{
if((int)c > 127)continue;
if((int)c < 32)continue;
sb.Append(c);
}
return sb.ToString();
}
private String Clean(String word)
{
String[] replaceList={"\"","?","~","|",":",","};
// String[] replaceList={"(",")","\"","?","~","|",":",","};
foreach(String toReplace in replaceList)
{
word=word.Replace(toReplace,null);
}
return word;
}
private String CleanStartsWithEndsWith(String word)
{
String[] replaceList={"'","`","[","]","!"};
foreach(String toReplace in replaceList)
{
if(word.StartsWith(toReplace))word=word.Substring(1);
if(word.EndsWith(toReplace))word=word.Substring(0,word.Length-2);
}
return word;
}
private bool KeepStartsWithEndsWith(String word)
{
String[] replaceList={".","~","1","2","3","4","5","6","7","8","9","0"};
foreach(String toReplace in replaceList)
{
if(word.StartsWith(toReplace))return false;
if(word.EndsWith(toReplace))return false;
}
return true;
}
private bool IsNumber(String word)
{
double result=double.NaN;
return double.TryParse(word,out result);
}
}
}

View File

@@ -0,0 +1,409 @@
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;
using System.Reflection;
//using System.Runtime.Remoting.Messaging;
using System.Text;
using MarketData.MarketDataModel;
namespace MarketData.Generator
{
public class SignalGenerator
{
private SignalGenerator()
{
}
public static Signals GenerateSignals(DMAPrices shortMA, DMAPrices longMA)
{
try
{
Signals signals = new Signals();
Align(shortMA, longMA);
for (int index = shortMA.Count - 1; index >= 0; index--)
{
Signal currSignal = new Signal();
Signal prevSignal = null;
DMAPrice prevShortData = null;
DMAPrice currShortData = shortMA[index];
DMAPrice prevLongData = null;
DMAPrice currLongData = longMA[index];
currSignal.Ticker = currShortData.Symbol;
currSignal.SignalDate = currShortData.Date;
if (index == shortMA.Count - 1)
{
if (currShortData.AVGPrice > currLongData.AVGPrice)
{
currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
currSignal.Reason = "Crossover.";
}
else
{
currSignal.SignalIndicator = Signal.Indicator.StrongSell;
currSignal.Reason = "Crossover.";
}
signals.Add(currSignal);
continue;
}
prevSignal = (Signal)signals[signals.Count - 1];
prevShortData = shortMA[index + 1];
prevLongData = longMA[index + 1];
if (currShortData.AVGPrice > currLongData.AVGPrice)
{
if (prevShortData.AVGPrice < prevLongData.AVGPrice)
{
currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
currSignal.Reason = "Crossover.";
}
else
{
currSignal.SignalIndicator = Signal.Indicator.WeakBuy;
currSignal.Reason = "Existing Crossover maintained.";
}
}
else
{
if (prevShortData.AVGPrice > prevLongData.AVGPrice)
{
currSignal.SignalIndicator = Signal.Indicator.StrongSell;
currSignal.Reason = "Crossover.";
}
else
{
currSignal.SignalIndicator = Signal.Indicator.WeakSell;
currSignal.Reason = "Existing Crossover maintained.";
}
}
signals.Add(currSignal);
}
return signals;
}
catch (Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception);
return null;
}
}
public static Signals GenerateSignals(Stochastics stochastics)
{
try
{
Signals signals = new Signals();
StochasticElement prevStochastic = null;
Signal prevSignal = null;
bool crossover = false;
for (int index = stochastics.Count - 1; index >= 0; index--)
{
Signal currSignal = new Signal();
StochasticElement currStochastic = stochastics[index];
currSignal.Ticker = currStochastic.Symbol;
currSignal.SignalDate = currStochastic.Date;
// identify the crossover
if (currStochastic.PK > currStochastic.PD && (null != prevStochastic && prevStochastic.PK <= prevStochastic.PD))
{
currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
currSignal.Reason = "currStochastic.PK>currStochastic.PD";
crossover = true;
}
else if (currStochastic.PK <= currStochastic.PD && (null != prevStochastic && prevStochastic.PK > prevStochastic.PD))
{
currSignal.SignalIndicator = Signal.Indicator.StrongSell;
currSignal.Reason = "currStochastic.PK <= currStochastic.PD";
crossover = true;
}
else if (currStochastic.PKInRange(20, 50))
{
crossover = false;
if (null != prevStochastic && prevStochastic.PK < 20)
{
currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
currSignal.Reason = "currStochastic.PKInRange(20, 50),prevStochastic.PK < 20 ";
}
else if (null != prevStochastic && prevStochastic.PK > 50)
{
currSignal.SignalIndicator = Signal.Indicator.WeakSell;
currSignal.Reason = "currStochastic.PKInRange(20, 50),prevStochastic.PK > 50";
}
else
{
currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
currSignal.Reason = "currStochastic.PKInRange(20, 50),prevStochastic.PKInRange(20,50)";
}
}
else if (currStochastic.PKInRange(51, 79))
{
crossover = false;
if (null != prevStochastic && prevStochastic.PK < 51)
{
currSignal.SignalIndicator = Signal.Indicator.WeakBuy;
currSignal.Reason = "currStochastic.PKInRange(51, 79),prevStochastic.PK < 51";
}
else if (null != prevStochastic && prevStochastic.PK > 79)
{
currSignal.SignalIndicator = Signal.Indicator.StrongSell;
currSignal.Reason = "currStochastic.PKInRange(51, 79),prevStochastic.PK > 79";
}
else
{
currSignal.SignalIndicator = Signal.Indicator.WeakBuy;
currSignal.Reason = "currStochastic.PKInRange(51, 79),prevStochastic.PKInRange(51,79)";
}
}
else if (currStochastic.PKInRange(80, 100))
{
crossover = false;
if (null != prevStochastic && prevStochastic.PK < 80)
{
currSignal.SignalIndicator = Signal.Indicator.WeakBuy;
currSignal.Reason = "currStochastic.PKInRange(80,100),prevStochastic.PK < 80";
}
else if (null != prevStochastic && prevStochastic.PK > 90)
{
currSignal.SignalIndicator = Signal.Indicator.StrongSell;
currSignal.Reason = "currStochastic.PKInRange(80,100),prevStochastic.PK > 90";
}
else
{
currSignal.SignalIndicator = Signal.Indicator.WeakBuy;
currSignal.Reason = "currStochastic.PKInRange(80,100),prevStochastic.PK >80,prevStochastic.PK<90";
}
}
else
{
if (crossover)
{
currSignal.SignalIndicator = prevSignal.SignalIndicator;
currSignal.Reason = "currStochastic<20, previous crossover holds.";
}
else
{
currSignal.SignalIndicator = Signal.Indicator.StrongSell;
currSignal.Reason = "currStochastic<20, no crossover identified";
}
}
signals.Add(currSignal);
prevStochastic = currStochastic;
prevSignal = (Signal)signals[signals.Count - 1];
}
return signals;
}
catch (Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception);
return null;
}
}
public static Signals GenerateSignals(MACDSignals macdSignals)
{
try
{
Signals signals = new Signals();
SignHolder signMACD = new SignHolder();
SignHolder signSignal = new SignHolder();
for (int index = macdSignals.Count - 1; index >= 0; index--)
{
Signal currSignal = new Signal();
Signal prevSignal = null;
MACDSignal prevSecurityData = null;
MACDSignal currSecurityData = macdSignals[index];
currSignal.Ticker = currSecurityData.Symbol;
currSignal.SignalDate = currSecurityData.Date;
if (index == macdSignals.Count - 1)
{
signMACD.SetValue(currSecurityData.MACD);
signSignal.SetValue(currSecurityData.Signal);
if (currSecurityData.MACD > currSecurityData.Signal)
{
if (currSecurityData.MACD < 0)
{
currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
currSignal.Reason = "MACD is above the signal line and the MACD is less than zero.";
}
else
{
currSignal.SignalIndicator = Signal.Indicator.WeakBuy;
currSignal.Reason = "MACD is above the signal line and the MACD is greater than or equal to zero.";
}
}
else
{
if (currSecurityData.MACD > 0)
{
currSignal.SignalIndicator = Signal.Indicator.StrongSell;
currSignal.Reason = "MACD is less than or equal to the signal line and MACD is above zero.";
}
else
{
currSignal.SignalIndicator = Signal.Indicator.WeakSell;
currSignal.Reason = "MACD is less than or equal to the signal line and MACD is less than or equal to zero";
}
}
signals.Add(currSignal);
continue;
}
prevSignal = (Signal)signals[signals.Count - 1];
prevSecurityData = macdSignals[index + 1];
signMACD.SetValue(currSecurityData.MACD);
signSignal.SetValue(currSecurityData.Signal);
//MACD is above the signal line
if (currSecurityData.MACD > currSecurityData.Signal)
{
// previous MACD is above signal line
if (prevSignal.IsBuy())
{
// the histogram is widening so keep it a strong buy
if (Math.Abs(currSecurityData.Histogram) > Math.Abs(prevSecurityData.Histogram) && currSecurityData.MACD < 0)
{
currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
currSignal.Reason = "The current MACD is above the signal and the histogram is widening and MACD is below zero";
}
else if (Math.Abs(currSecurityData.Histogram) > Math.Abs(prevSecurityData.Histogram))
{
currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
currSignal.Reason = "The current MACD is above the signal and the histogram is widening.";
}
else if (currSecurityData.MACD < 0)
{
currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
currSignal.Reason = "The current MACD is above the signal and less than zero.";
}
else
{
currSignal.SignalIndicator = Signal.Indicator.WeakBuy;
currSignal.Reason = "The current MACD is above the signal line but so was the previous MACD.";
}
}
else
{
if (currSecurityData.MACD > 0 && currSecurityData.Signal < 0)
{
currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
currSignal.Reason = "The current MACD is above the signal, MACD is above zero, signal is below zero.";
}
else if (currSecurityData.MACD > 0)
{
currSignal.SignalIndicator = Signal.Indicator.WeakBuy;
currSignal.Reason = "The current MACD is above the signal but the MACD is above zero.";
}
else
{
currSignal.SignalIndicator = Signal.Indicator.StrongBuy;
currSignal.Reason = "The current MACD is above the signal and the MACD is less than zero";
}
}
}
//MACD is below the signal line
else
{
// check previous MACD is below the signal line
// The MACD line is above the signal line but the MACD is below zero so this is considered a weaker buy signal
if (prevSignal.IsSell())
{
if (Math.Abs(currSecurityData.Histogram) > Math.Abs(prevSecurityData.Histogram) && currSecurityData.MACD > 0)
{
currSignal.SignalIndicator = Signal.Indicator.StrongSell;
currSignal.Reason = "The current MACD is below or equal to the signal, the histogram is widening, and MACD is above zero";
}
else if (Math.Abs(currSecurityData.Histogram) > Math.Abs(prevSecurityData.Histogram))
{
currSignal.SignalIndicator = Signal.Indicator.StrongSell;
currSignal.Reason = "The current MACD is below or equal to the signal, the histogram is widening.";
}
else if (currSecurityData.MACD > 0)
{
currSignal.SignalIndicator = Signal.Indicator.StrongSell;
currSignal.Reason = "The current MACD is below or equal to the signal line and MACD is above zero";
}
else
{
currSignal.SignalIndicator = Signal.Indicator.WeakSell;
currSignal.Reason = "The current MACD is below or equal to the signal line.";
}
}
else
{
if (currSecurityData.MACD < 0 && currSecurityData.Signal > 0)
{
currSignal.SignalIndicator = Signal.Indicator.StrongSell;
currSignal.Reason = "The current MACD is below or equal to the signal line, MACD is below zero, signal is above zero";
}
else if (currSecurityData.MACD < 0)
{
currSignal.SignalIndicator = Signal.Indicator.WeakSell;
currSignal.Reason = "The current MACD is below or equal to the signal line and MACD is below zero.";
}
else
{
currSignal.SignalIndicator = Signal.Indicator.StrongSell;
currSignal.Reason = "The current MACD is below or equal to the signal and MACD is greater than or equal to zero.";
}
}
}
signals.Add(currSignal);
}
signals.Sort(new SignalComparatorByDateDescending()); // This was added to get the most recent dated items into the lowest position of the returned array. Users of this function were performing TAKE() on the array to get the topmost nth items and winding up with the bottommost (furthest dated) items.
return signals;
}
catch (Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception);
return null;
}
}
private static void Align(DMAPrices shortMA, DMAPrices longMA)
{
Dictionary<DateTime, DMAPrice> shortMADates = new Dictionary<DateTime, DMAPrice>();
Dictionary<DateTime, DMAPrice> longMADates = new Dictionary<DateTime, DMAPrice>();
List<DateTime> shortMAPriceToRemove = new List<DateTime>();
List<DateTime> longMAPriceToRemove = new List<DateTime>();
for (int index = 0; index < shortMA.Count; index++)
{
DMAPrice dmaPrice = shortMA[index];
if (!shortMADates.ContainsKey(dmaPrice.Date)) shortMADates.Add(dmaPrice.Date, dmaPrice);
}
for (int index = 0; index < longMA.Count; index++)
{
DMAPrice dmaPrice = longMA[index];
if (!longMADates.ContainsKey(dmaPrice.Date)) longMADates.Add(dmaPrice.Date, dmaPrice);
}
for (int index = 0; index < shortMA.Count; index++)
{
DMAPrice dmaPrice = shortMA[index];
if (!longMADates.ContainsKey(dmaPrice.Date)) shortMAPriceToRemove.Add(dmaPrice.Date);
}
for (int index = 0; index < longMA.Count; index++)
{
DMAPrice dmaPrice = longMA[index];
if (!shortMADates.ContainsKey(dmaPrice.Date)) longMAPriceToRemove.Add(dmaPrice.Date);
}
for (int index = 0; index < shortMAPriceToRemove.Count; index++)
{
DateTime dateToRemove = shortMAPriceToRemove[index];
DMAPrice dmaPrice = shortMADates[dateToRemove];
shortMA.Remove(dmaPrice);
}
for (int index = 0; index < longMAPriceToRemove.Count; index++)
{
DateTime dateToRemove = longMAPriceToRemove[index];
DMAPrice dmaPrice = longMADates[dateToRemove];
longMA.Remove(dmaPrice);
}
}
private static void PrintSignals(ArrayList signals)
{
for(int index=0;index<signals.Count;index++)
{
Signal signal=(Signal)signals[index];
MDTrace.WriteLine(LogLevel.DEBUG,signal.ToString());
}
}
}
}

View File

@@ -0,0 +1,177 @@
using System;
using System.Text;
using System.Collections.Generic;
using MarketData.DataAccess;
using MarketData.MarketDataModel;
using MarketData.Utils;
namespace MarketData.Generator
{
public class SignalTrader
{
private SignalTrader()
{
}
// stopLossThreshold is a percentage that minimizes the loss on the sell side and sells on the Sell signal if the last
// trade price dips below the offering price.
public static Portfolio TradeSignals(DateRange tradeDates,Signals signals, Prices prices, double initialCash, double stopLossThreshold, int stopLossDays)
{
Dictionary<DateTime, Price> pricesByDate = new Dictionary<DateTime, Price>();
Dictionary<DateTime, Signal> signalsByDate = new Dictionary<DateTime, Signal>();
Portfolio portfolio = new Portfolio(initialCash);
for (int index = 0; index < prices.Count; index++)
{
Price price = prices[index];
pricesByDate.Add(price.Date, price);
}
for (int index = 0; index < signals.Count; index++)
{
Signal signal = signals[index];
signalsByDate.Add(signal.SignalDate, signal);
}
// portfolio.AvailableCash = initialCash;
for (int index = 0; index < tradeDates.Count; index++)
{
DateTime tradingDate = tradeDates[index];
if (!pricesByDate.ContainsKey(tradingDate)) continue;
if (!signalsByDate.ContainsKey(tradingDate)) continue;
Signal signal = signalsByDate[tradingDate];
if (signal.IsStrongBuy() && !portfolio.HasOpenPosition)
{
if (portfolio.AvailableCash > 0)
{
ModelTrade trade = new ModelTrade();
Price price = pricesByDate[signal.SignalDate];
trade.Symbol = price.Symbol;
trade.Price = price.Close;
trade.Shares = (int)(portfolio.AvailableCash / trade.Price);
trade.TradeDate = price.Date;
trade.Exposure = trade.Shares * trade.Price;
trade.Type = ModelTrade.TradeType.Buy;
portfolio.AvailableCash -= (trade.Price * trade.Shares);
portfolio.Add(trade);
portfolio.HasOpenPosition = true;
}
}
else if (signal.IsStrongSell() && portfolio.HasOpenPosition)
{
ModelTrade trade = new ModelTrade();
ModelTrade lastTrade = portfolio.GetLastTrade();
Price price = pricesByDate[signal.SignalDate];
if (0.00 == stopLossThreshold && price.Close < lastTrade.Price) continue;
int daysHeld = (price.Date - lastTrade.TradeDate).Days;
double stopLossPrice = lastTrade.Price - (lastTrade.Price * stopLossThreshold);
bool stopLossIndicator = price.Close < stopLossPrice && daysHeld > stopLossDays ? true : false;
if (!stopLossIndicator && price.Close < lastTrade.Price) continue;
else if (stopLossIndicator) trade.Comment = "Stop Loss. price decrease " + String.Format("{0:p}", stopLossThreshold) + ", holding days (" + daysHeld + ") >" + stopLossDays;
portfolio.AvailableCash += ((lastTrade.Shares * price.Close));
trade.Symbol = price.Symbol;
trade.Price = price.Close;
trade.Shares = (int)(portfolio.AvailableCash / trade.Price);
trade.Type = ModelTrade.TradeType.Sell;
trade.TradeDate = price.Date;
double marketValue = (trade.Price * trade.Shares);
// trade.GainLoss = (trade.Price * trade.Shares) - (lastTrade.Price * lastTrade.Shares);
trade.GainLoss = marketValue - lastTrade.Exposure;
trade.Return = ((marketValue - lastTrade.Exposure) / lastTrade.Exposure);
trade.DaysHeld = daysHeld;
portfolio.Add(trade);
portfolio.HasOpenPosition = false;
}
}
return portfolio;
}
// Add the MA(10,50)
// if (8,17,9) signals a buy and we are on the + side of the MA(10,50) then seek support from the (12,26,9) which must be a buy signal
// if (8,17,9) signals a buy and we are on the - side of the MA(10,50) then no support is necessary.
// sell when the (8,17,9) signals a sell
public static Portfolio TradeSignals(DateRange tradeDates,Signals signals, Signals supportSignals, Signals crossoverSignals,Prices prices, double initialCash)
{
Dictionary<DateTime, Price> pricesByDate = new Dictionary<DateTime, Price>();
Dictionary<DateTime, Signal> supportSignalsByDate = new Dictionary<DateTime, Signal>();
Dictionary<DateTime, Signal> signalsByDate = new Dictionary<DateTime, Signal>();
Dictionary<DateTime, Signal> crossoverSignalsByDate = new Dictionary<DateTime, Signal>();
Signal prevSignal = null;
MDTrace.WriteLine(LogLevel.DEBUG,"[SignalTrader::TradeSignals]");
Portfolio portfolio = new Portfolio(initialCash);
int holdingDays = 0;
int tradeCount = 0;
int averageHoldingDays = 0;
for (int index = 0; index < prices.Count; index++)
{
Price price = prices[index];
pricesByDate.Add(price.Date, price);
}
for (int index = 0; index < supportSignals.Count; index++)
{
Signal supportSignal = supportSignals[index];
supportSignalsByDate.Add(supportSignal.SignalDate, supportSignal);
}
for (int index = 0; index < signals.Count; index++)
{
Signal signal = signals[index];
signalsByDate.Add(signal.SignalDate, signal);
}
for (int index = 0; index < crossoverSignals.Count; index++)
{
Signal signal = crossoverSignals[index];
crossoverSignalsByDate.Add(signal.SignalDate, signal);
}
// portfolio.AvailableCash = initialCash;
for(int index=0;index<tradeDates.Count;index++)
{
DateTime tradingDate = tradeDates[index];
if (!pricesByDate.ContainsKey(tradingDate)) continue;
if (!supportSignalsByDate.ContainsKey(tradingDate)) continue;
if (!signalsByDate.ContainsKey(tradingDate)) continue;
if(!crossoverSignalsByDate.ContainsKey(tradingDate))continue;
Signal signal = signalsByDate[tradingDate];
Signal supportSignal = supportSignalsByDate[tradingDate];
Signal crossoverSignal=crossoverSignalsByDate[tradingDate];
Price price = pricesByDate[signal.SignalDate];
if(signal.IsStrongBuy() && portfolio.AvailableCash>0 &&!portfolio.HasOpenPosition)
{
if (null != prevSignal && prevSignal.IsStrongBuy()) continue;
if (crossoverSignal.IsBuy() && !supportSignal.IsBuy()) continue;
ModelTrade trade = new ModelTrade();
trade.Symbol = price.Symbol;
trade.Price = price.Close;
trade.Shares = (int)(portfolio.AvailableCash / trade.Price);
trade.TradeDate = price.Date;
trade.Type = ModelTrade.TradeType.Buy;
trade.Exposure = trade.Shares * trade.Price;
portfolio.AvailableCash -= (trade.Price * trade.Shares);
portfolio.Add(trade);
portfolio.HasOpenPosition = true;
}
else if (signal.IsStrongSell() && portfolio.HasOpenPosition)
{
ModelTrade trade = new ModelTrade();
ModelTrade lastTrade = portfolio.GetLastTrade();
if (price.Close < lastTrade.Price) continue;
portfolio.AvailableCash += ((lastTrade.Shares * price.Close));
trade.Symbol = price.Symbol;
trade.Price = price.Close;
trade.Shares = (int)(portfolio.AvailableCash / trade.Price);
trade.Type = ModelTrade.TradeType.Sell;
trade.TradeDate = price.Date;
double marketValue = trade.Price * trade.Shares;
trade.GainLoss = marketValue - lastTrade.Exposure;
trade.Return = (marketValue - lastTrade.Exposure) / lastTrade.Exposure;
trade.DaysHeld = (price.Date - lastTrade.TradeDate).Days; ;
portfolio.Add(trade);
portfolio.HasOpenPosition = false;
tradeCount++;
holdingDays += (price.Date - lastTrade.TradeDate).Days;
averageHoldingDays = holdingDays / tradeCount;
}
prevSignal = signal;
}
return portfolio;
}
}
}

View File

@@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using MarketData.Generator.MovingAverage;
using MarketData.MarketDataModel;
// Filename: StochasticsGenerator.cs
// Author:Sean Kessler
// Date:08/2013
namespace MarketData.Generator
{
/// <summary>StochasticsGenerator - StochasticsGenerator utility class</summary>
public class StochasticsGenerator
{
public static Stochastics GenerateStochastics(Prices prices, int periodN=9,int periodX=3)
{
try
{
Stochastics stochastics = new Stochastics();
DMAPricesByDate dmaLNPricesByDate = null; // these are the min(low) for the period-n
DMAPricesByDate dmaHNPricesByDate = null; // these are the max(high) for the period-n
DMAPrices dmaLN = MovingAverageGenerator.GenerateMovingMinsOnLow(prices, periodN);
DMAPrices dmaHN = MovingAverageGenerator.GenerateMovingMaxOnHigh(prices, periodN);
dmaLNPricesByDate = dmaLN.GetDMAPricesByDate();
dmaHNPricesByDate = dmaHN.GetDMAPricesByDate();
int stopCount;
stopCount = Math.Max(periodN, periodX)+1; // clamp the solution set the range
for (int index = 0; index < prices.Count - stopCount; index++)
{
Price price = prices[index];
StochasticElement stochasticElement = new StochasticElement();
if(!dmaLNPricesByDate.ContainsKey(price.Date))continue;
if(!dmaHNPricesByDate.ContainsKey(price.Date))continue;
stochasticElement.Date = price.Date;
stochasticElement.Symbol = price.Symbol;
stochasticElement.Open = price.Open;
stochasticElement.High = price.High;
stochasticElement.Low = price.Low;
stochasticElement.Close = price.Close;
stochasticElement.LN = dmaLNPricesByDate[price.Date].MinPrice; // min low for period N
stochasticElement.HN = dmaHNPricesByDate[price.Date].MaxPrice; // max high for period N
stochasticElement.HX = CalculateHX(index, prices, dmaLN, periodX); // sum (CCL-LN) for past periodX
stochasticElement.LX = CalculateLX(index, dmaHN, dmaLN, periodX); // sum(HN-LN) for past periodX
stochasticElement.PK = 100 * (price.Close - stochasticElement.LN) / (stochasticElement.HN - stochasticElement.LN); // 100*(CCL-LN)/(HN-LN)
stochasticElement.PD = 100 * (stochasticElement.HX / stochasticElement.LX); // 100*(HX/LX)
stochastics.Add(stochasticElement);
}
return stochastics;
}
catch (Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception);
return null;
}
}
// calculate the x-period sum of (CCL-LN)
private static double CalculateHX(int startingIndex,Prices prices,DMAPrices lnPrices,int periodK)
{
try
{
double sum = 0.00;
for (int index = startingIndex; index < startingIndex + periodK; index++)
{
Price price = prices[index];
DMAPrice lnPrice = lnPrices[index];
sum+=(price.Close-lnPrice.MinPrice);
}
return sum;
}
catch(Exception /*exception*/)
{
return double.NaN;
}
}
//sum(HN-LN) for past period K
private static double CalculateLX(int startingIndex,DMAPrices hnPrices,DMAPrices lnPrices, int periodK)
{
try
{
double sum = 0.00;
for (int index = startingIndex; index < startingIndex + periodK; index++)
{
DMAPrice hnPrice = hnPrices[index];
DMAPrice lnPrice = lnPrices[index];
sum+=(hnPrice.MaxPrice-lnPrice.MinPrice);
}
return sum;
}
catch (Exception /*exception*/)
{
return double.NaN;
}
}
}
}

View File

@@ -0,0 +1,204 @@
using MarketData.DataAccess;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MarketData.MarketDataModel
{
// ********************************************************************************************************************************************************
// ******************************************** U N I T T E S T F O R J O E L G R E E N B L A T T M E T H O D O L O G Y ***************************
// ********************************************************************************************************************************************************
public class TLBItemList : List<TLBItem>
{
public TLBItemList()
{
}
public TLBItemList(TLBItemList itemList)
{
foreach(TLBItem item in itemList)Add(item);
}
public static void ApplyTLBRanks(TLBItemList items)
{
Dictionary<String,TLBItem> itemsBySymbol=new Dictionary<String,TLBItem>();
Dictionary<String,int> itemsRankedByROC=new Dictionary<String,int>();
Dictionary<String,int> itemsRankedByROIC=new Dictionary<String,int>();
Dictionary<String,int> itemsRankedByEarningsYield=new Dictionary<String,int>();
foreach(TLBItem item in items)
{
if(!itemsBySymbol.ContainsKey(item.Symbol))
{
itemsBySymbol.Add(item.Symbol,item);
}
}
TLBItemList itemsCopyByROC=new TLBItemList(items);
TLBItemList itemsCopyByROIC=new TLBItemList(items);
TLBItemList itemsCopyByEarningsYield=new TLBItemList(items);
itemsCopyByROC.Sort(new TLBListByROC());
itemsCopyByROIC.Sort(new TLBListByROIC());
itemsCopyByEarningsYield.Sort(new TLBListByEarningsYield());
for(int index=0;index<itemsCopyByROC.Count;index++)
{
String symbol=itemsCopyByROC[index].Symbol;
if(!itemsRankedByROC.ContainsKey(symbol))itemsRankedByROC.Add(symbol,index);
}
for(int index=0;index<itemsCopyByROIC.Count;index++)
{
String symbol=itemsCopyByROIC[index].Symbol;
if(!itemsRankedByROIC.ContainsKey(symbol))itemsRankedByROIC.Add(symbol,index);
}
for(int index=0;index<itemsCopyByEarningsYield.Count;index++)
{
String symbol=itemsCopyByEarningsYield[index].Symbol;
if(!itemsRankedByEarningsYield.ContainsKey(symbol))itemsRankedByEarningsYield.Add(symbol,index);
}
for(int index=0;index<items.Count;index++)
{
TLBItem tlbItem=items[index];
tlbItem.TLBRankROC=itemsRankedByROC[tlbItem.Symbol]+itemsRankedByEarningsYield[tlbItem.Symbol];
tlbItem.TLBRankROIC=itemsRankedByROIC[tlbItem.Symbol]+itemsRankedByEarningsYield[tlbItem.Symbol];
}
}
}
public class TLBListByROC : IComparer<TLBItem>
{
public int Compare(TLBItem v1,TLBItem v2)
{
if(double.IsNaN(v1.ROC)&&double.IsNaN(v2.ROC))return 0;
else if(double.IsNaN(v1.ROC))return -1;
else if(double.IsNaN(v2.ROC))return 1;
return v1.ROC.CompareTo(v2.ROC);
}
}
public class TLBListByROIC : IComparer<TLBItem>
{
public int Compare(TLBItem v1,TLBItem v2)
{
if(double.IsNaN(v1.ROIC)&&double.IsNaN(v2.ROIC))return 0;
else if(double.IsNaN(v1.ROIC))return -1;
else if(double.IsNaN(v2.ROIC))return 1;
return v1.ROIC.CompareTo(v2.ROIC);
}
}
public class TLBListByEarningsYield : IComparer<TLBItem>
{
public int Compare(TLBItem v1,TLBItem v2)
{
if(double.IsNaN(v1.EarningsYield)&&double.IsNaN(v2.EarningsYield))return 0;
else if(double.IsNaN(v1.EarningsYield))return -1;
else if(double.IsNaN(v2.EarningsYield))return 1;
return v1.EarningsYield.CompareTo(v2.EarningsYield);
}
}
public class TLBItem
{
public String Symbol{get;set;}
public double EBIT{get;set;}
public double EarningsYield{get;set;}
public double EnterpriseValue{get;set;}
public double WorkingCapital{get;set;}
public double PPE{get;set;}
public double ROIC{get;set;}
public double ROC{get;set;}
public double TLBRankROIC{get;set;}
public double TLBRankROC{get;set;}
}
// The sorting of the ranks will be from lowest to highest. The largest ROICS will have the largest ranks and the largest EarningsYields will have the largest ranks.
// This sorting is opposite to how Joel sorted his lists. Joel's lists wind up with the best candidates having the lowest ranks while this one winds up with the
// best candidates having the highest ranks. This can be seen when visiting the Valuations view and changing the sort to TLBRankROIC with the highest valued ranks
// on top. These will be the top candidates.
public class TLBRankGenerator
{
private TLBRankGenerator()
{
}
// *******************************************************************************************************************************************************************
// ********************************************** T H E L I T T L E B O O K R A N K G E N E R A T O R U N I T T E S T **** **********************************
// *******************************************************************************************************************************************************************
public static TLBItemList ApplyTLBRanks()
{
List<String> symbols=PricingDA.GetSymbols();
TLBItemList tlbList=new TLBItemList();
foreach(String symbol in symbols)
{
double earningsYield=double.NaN;
double roc=double.NaN;
double roic=double.NaN;
Console.WriteLine("Working on "+symbol);
BalanceSheet balanceSheet = BalanceSheetDA.GetBalanceSheet(symbol,BalanceSheet.PeriodType.Annual);
TimeSeriesCollection workingCapitalCollection = HistoricalDA.GetTimeSeries(symbol, TimeSeriesElement.ElementType.WorkingCapital);
TimeSeriesCollection roicCollection = HistoricalDA.GetTimeSeries(symbol, TimeSeriesElement.ElementType.ROIC);
Fundamental fundamental=FundamentalDA.GetFundamental(symbol);
if(null==fundamental)continue;
TLBItem tlbItem=new TLBItem();
if(!double.IsNaN(fundamental.EBIT)&&!double.IsNaN(fundamental.EnterpriseValue)&&fundamental.EnterpriseValue>0.00&&fundamental.EBIT>0.00)
{
earningsYield=fundamental.EBIT/fundamental.EnterpriseValue;
}
if(!double.IsNaN(fundamental.EBIT)&&null!=balanceSheet&&!double.IsNaN(balanceSheet.NetFixedAssets))
{
roc=fundamental.EBIT/balanceSheet.NetFixedAssets;
}
if(null!=roicCollection && roicCollection.Count>0)roic=roicCollection[0].Value;
tlbItem.Symbol=symbol;
tlbItem.EBIT=fundamental.EBIT;
tlbItem.EarningsYield=earningsYield;
tlbItem.EnterpriseValue=fundamental.EnterpriseValue;
tlbItem.ROC=roc*100.00;
tlbItem.ROIC=roic;
tlbList.Add(tlbItem);
}
TLBItemList.ApplyTLBRanks(tlbList);
return tlbList;
}
// *******************************************************************************************************************************************************************
// ********************************************** T H E L I T T L E B O O K R A N K G E N E R A T O R F O R V A L U A T I O N **********************************
// *******************************************************************************************************************************************************************
public static void ApplyTLBRanks(Valuations valuations)
{
Dictionary<String,Valuation> valuationsBySymbol=new Dictionary<String,Valuation>();
Dictionary<String,int> valuationsRankedByROIC=new Dictionary<String,int>();
Dictionary<String,int> valuationsRankedByROC=new Dictionary<String,int>();
Dictionary<String,int> valuationsRankedByEarningsYield=new Dictionary<String,int>();
foreach(Valuation valuation in valuations)
{
if(!valuationsBySymbol.ContainsKey(valuation.Symbol))valuationsBySymbol.Add(valuation.Symbol,valuation);
}
Valuations valuationsCopyByROIC=new Valuations(valuations);
valuationsCopyByROIC.Sort(new ValuationsByROIC());
Valuations valuationsCopyByROC=new Valuations(valuations);
valuationsCopyByROC.Sort(new ValuationsByROC());
Valuations valuationsCopyByEarningsYield=new Valuations(valuations);
valuationsCopyByEarningsYield.Sort(new ValuationsByEarningsYield());
for(int index=0;index<valuationsCopyByROIC.Count;index++)
{
String symbol=valuationsCopyByROIC[index].Symbol;
if(!valuationsRankedByROIC.ContainsKey(symbol))valuationsRankedByROIC.Add(symbol,index);
}
for(int index=0;index<valuationsCopyByROC.Count;index++)
{
String symbol=valuationsCopyByROC[index].Symbol;
if(!valuationsRankedByROC.ContainsKey(symbol))valuationsRankedByROC.Add(symbol,index);
}
for(int index=0;index<valuationsCopyByEarningsYield.Count;index++)
{
String symbol=valuationsCopyByEarningsYield[index].Symbol;
if(!valuationsRankedByEarningsYield.ContainsKey(symbol))valuationsRankedByEarningsYield.Add(symbol,index);
}
for(int index=0;index<valuations.Count;index++)
{
Valuation valuation=valuations[index];
valuation.TLBRankROIC=valuationsRankedByROIC[valuation.Symbol]+valuationsRankedByEarningsYield[valuation.Symbol];
valuation.TLBRankROC=valuationsRankedByROC[valuation.Symbol]+valuationsRankedByEarningsYield[valuation.Symbol];
}
}
}
}

View File

@@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MarketData.Generator.TermStructure
{
public class CurveData
{
public CurveData() { ;}
public String CurveName{get;set;}
public DateTime PricingDate { get; set; }
public Tenors CurveTenors { get; set; }
public String GetTenorValue(String tenorName)
{
try
{
int index=CurveTenors.FindIndex(x => x.Item.Equals(tenorName));
if (-1 == index || index >= CurveTenors.Count) return null;
return CurveTenors[index].Value;
}
catch (Exception /*exception*/)
{
return null;
}
}
public void RemoveTenors(String strTenors)
{
string[] strTenorArray = strTenors.Split(',');
foreach (String strTenor in strTenorArray)
{
CurveTenors.Remove(strTenor);
}
}
}
// *************************************************************8
public class CurveMappings : List<CurveMapping>
{
public CurveMappings()
{
}
}
public class CurveMapping
{
public CurveMapping()
{
}
public String CurveName { get; set; }
public String Currency { get; set; }
public String BloombergMnemonic { get; set; }
public String Field { get; set; }
public String AttributeName { get; set; }
public String CurveId { get; set; }
public Tenors TenorMappings{get;set;}
public DateTime PricingDate { get; set; }
public void SetTenorValues(String strValues)
{
String[] strTenorValues = strValues.Split(',');
if (strTenorValues.Length != TenorMappings.Count) throw new Exception("Tenor mapping count does not match tenor value count");
for (int index = 0; index < TenorMappings.Count; index++)
{
TenorMappings[index].Value = strTenorValues[index];
}
}
}
public class Tenors : List<Tenor>
{
public Tenors()
{
}
public void Remove(String strTenor)
{
for(int index=0;index<Count;index++)
{
if (this[index].Item.Equals(strTenor))
{
RemoveAt(index);
return;
}
}
}
public static Tenors FromString(String strTenors)
{
Tenors tenors = new Tenors();
String[] strTenorsArray = strTenors.Split(',');
for (int index = 0; index < strTenorsArray.Length; index++)
{
tenors.Add(new Tenor(strTenorsArray[index]));
}
return tenors;
}
public String GetTenorValues()
{
StringBuilder sb = new StringBuilder();
for (int index = 0; index < this.Count;index++)
{
String value = this[index].Value;
if(null!=value)sb.Append(value);
if (index < this.Count - 1) sb.Append(",");
}
return sb.ToString();
}
}
public class Tenor
{
public Tenor()
{
}
public Tenor(String tenor, String value)
{
Item = tenor;
Value = value;
}
public Tenor(String tenor)
{
Item = tenor;
}
public String Item { get; set; }
public String Value { get; set; }
public double ValueNum{get{return double.Parse(Value);}}
public int Days
{
get
{
return GetTenorDays(this.Item);
}
}
public static int GetTenorDays(String strTenor)
{
int multiplier = 1;
strTenor=strTenor.ToUpper();
if (strTenor.EndsWith("D")) multiplier = 1;
else if(strTenor.EndsWith("W")) multiplier = 7;
else if(strTenor.EndsWith("M")) multiplier = 30;
else if(strTenor.EndsWith("Y")) multiplier = 360;
strTenor = strTenor.Substring(0, strTenor.Length - 1);
return Int32.Parse(strTenor)*multiplier;
}
}
}

View File

@@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MarketData.Numerical;
using MarketData.DataAccess;
using MarketData.MarketDataModel;
using MarketData.Utils;
namespace MarketData.Generator.TermStructure
{
public class TermStructureInterpolatorByDate : Dictionary<DateTime,TermStructureInterpolator>
{
}
public class TermStructureInterpolator
{
private Dictionary<int, double> splineRates = new Dictionary<int, double>();
private CurveData curveData = null;
private DateTime tradeDate;
public TermStructureInterpolator(DateTime tradeDate)
{
this.tradeDate=tradeDate;
SetCurveData();
}
public DateTime TradeDate { get; private set; }
public CurveData CurveData
{
get
{
return curveData;
}
}
public Dictionary<int, double> SplineRates
{
get
{
return splineRates;
}
}
public double GetRate(int days)
{
int index = 0;
if (null == curveData) return double.NaN;
for (; index < curveData.CurveTenors.Count;index++ )
{
Tenor tenor = curveData.CurveTenors[index];
int tenorDays=Tenor.GetTenorDays(tenor.Item);
if(tenorDays == days) return double.Parse(tenor.Value);
if(tenorDays>days) break;
}
double differenceDays=days-Tenor.GetTenorDays(curveData.CurveTenors[index].Item);
double dailyRate=(curveData.CurveTenors[index].ValueNum - curveData.CurveTenors[index-1].ValueNum) / (curveData.CurveTenors[index].Days - curveData.CurveTenors[index-1].Days);
double rate = curveData.CurveTenors[index].ValueNum + (differenceDays * dailyRate);
return rate;
}
private void SetCurveData()
{
YieldCurveData yieldCurveData=YieldCurveDA.GetYieldCurve(tradeDate);
if(null==yieldCurveData)return;
curveData=new CurveData();
Tenors tenors=new Tenors();
curveData.CurveTenors=tenors;
tenors.Add(new Tenor("1D",Utility.FormatNumber(yieldCurveData.Mo1,2)));
tenors.Add(new Tenor("1M",Utility.FormatNumber(yieldCurveData.Mo1,2)));
tenors.Add(new Tenor("3M",Utility.FormatNumber(yieldCurveData.Mo3,2)));
tenors.Add(new Tenor("6M",Utility.FormatNumber(yieldCurveData.Mo6,2)));
tenors.Add(new Tenor("1Y",Utility.FormatNumber(yieldCurveData.Yr1,2)));
tenors.Add(new Tenor("2Y",Utility.FormatNumber(yieldCurveData.Yr2,2)));
tenors.Add(new Tenor("3Y",Utility.FormatNumber(yieldCurveData.Yr3,2)));
tenors.Add(new Tenor("5Y",Utility.FormatNumber(yieldCurveData.Yr5,2)));
tenors.Add(new Tenor("7Y",Utility.FormatNumber(yieldCurveData.Yr7,2)));
tenors.Add(new Tenor("10Y",Utility.FormatNumber(yieldCurveData.Yr10,2)));
tenors.Add(new Tenor("20Y",Utility.FormatNumber(yieldCurveData.Yr20,2)));
tenors.Add(new Tenor("30Y",Utility.FormatNumber(yieldCurveData.Yr30,2)));
List<Element> sourcePairs = new List<Element>();
List<Element> destPairs = new List<Element>();
sourcePairs.Add(new Element(0,double.Parse(curveData.GetTenorValue("1D"))));
sourcePairs.Add(new Element(30,double.Parse(curveData.GetTenorValue("1M"))));
sourcePairs.Add(new Element(90,double.Parse(curveData.GetTenorValue("3M"))));
sourcePairs.Add(new Element(180,double.Parse(curveData.GetTenorValue("6M"))));
sourcePairs.Add(new Element(360,double.Parse(curveData.GetTenorValue("1Y"))));
sourcePairs.Add(new Element(720,double.Parse(curveData.GetTenorValue("2Y"))));
sourcePairs.Add(new Element(1080,double.Parse(curveData.GetTenorValue("3Y"))));
sourcePairs.Add(new Element(1800,double.Parse(curveData.GetTenorValue("5Y"))));
sourcePairs.Add(new Element(2520,double.Parse(curveData.GetTenorValue("7Y"))));
sourcePairs.Add(new Element(3600,double.Parse(curveData.GetTenorValue("10Y"))));
sourcePairs.Add(new Element(7200,double.Parse(curveData.GetTenorValue("20Y"))));
sourcePairs.Add(new Element(10800,double.Parse(curveData.GetTenorValue("30Y"))));
for (int index = 0; index <= 10800; index++)destPairs.Add(new Element(index, 0));
Element[] sourcePairsArray = sourcePairs.ToArray();
Element[] destPairsArray = destPairs.ToArray();
CatmullRom.PerformSpline(sourcePairsArray,destPairsArray);
for (int index = 0; index < destPairsArray.Length; index++)
{
if (!splineRates.ContainsKey((int)destPairsArray[index].Column))
{
splineRates.Add((int)destPairsArray[index].Column, destPairsArray[index].Row);
}
}
}
}
// *************************************************************************************************************************************************************
public class TermStructureRatesGenerator
{
private TermStructureInterpolatorByDate termStructureInterpolatorByDate = new TermStructureInterpolatorByDate();
public TermStructureRatesGenerator()
{
}
public void Add(DateTime tradeDate)
{
if (termStructureInterpolatorByDate.ContainsKey(tradeDate)) return;
termStructureInterpolatorByDate.Add(tradeDate,new TermStructureInterpolator(tradeDate));
}
public CurveData GetTermStructureCurve(DateTime tradeDate)
{
if (null == termStructureInterpolatorByDate||!termStructureInterpolatorByDate.ContainsKey(tradeDate)) return null;
TermStructureInterpolator termStructureInterpolator = termStructureInterpolatorByDate[tradeDate];
return termStructureInterpolator.CurveData;
}
public double GetRate(DateTime tradeDate,int days)
{
if (!termStructureInterpolatorByDate.ContainsKey(tradeDate)) return double.NaN;
TermStructureInterpolator termStructureInterpolator = termStructureInterpolatorByDate[tradeDate];
return termStructureInterpolator.GetRate(days);
}
}
}

View File

@@ -0,0 +1,102 @@
//using System;
//using System.Collections.Generic;
//using System.Text;
//using MarketData.MarketDataModel;
//using MarketData.DataAccess;
//using MarketData.Utils;
//namespace MarketData.Generator
//{
// public delegate void ProgressHandler(String symbol);
// public class TradeSignalWatchGenerator
// {
// private TradeSignalWatchGenerator()
// {
// }
// public static List<TradeResult> GenerateTradeSignalWatchList(String watchListName, String initialInvestment, String historicalDate, String macd,ProgressHandler progressHandler)
// {
// try
// {
// List<TradeResult> tradeResultList = new List<TradeResult>();
// List<String> symbols = WatchListDA.GetWatchList(watchListName);
// foreach (String symbol in symbols)
// {
// if (null != progressHandler) progressHandler(symbol);
// TradeResult tradeResult = TradeSignalSymbolDateWithResult(symbol, initialInvestment, historicalDate, macd, null, null);
// if(null!=tradeResult)tradeResultList.Add(tradeResult);
// }
// tradeResultList.Sort();
// return tradeResultList;
// }
// catch (Exception exception)
// {
// MDTrace.WriteLine(LogLevel.DEBUG,exception);
// return null;
// }
// }
// private static TradeResult TradeSignalSymbolDateWithResult(String symbol, String initialInvestment, String strHistoricalDate, String macd, String strStopLossThreshold, String strStopLossDays)
// {
// try
// {
// TradeResult tradeResult = new TradeResult();
// DateGenerator dateGenerator = new DateGenerator();
// MACDSetup macdSetup = null;
// double stopLossThreshold = 0.00;
// int stopLossDays = 0;
// symbol = symbol.ToUpper();
// tradeResult.Symbol = symbol;
// DateRange tradeDates = new DateRange(DateTime.Parse(strHistoricalDate), DateTime.Now);
// if (null != strStopLossThreshold) stopLossThreshold = Double.Parse(strStopLossThreshold);
// else stopLossThreshold = 0;
// if (null != strStopLossDays) stopLossDays = Int32.Parse(strStopLossDays);
// else stopLossDays = 0;
// double initialCash = double.Parse(initialInvestment);
// DateTime historicalDate = DateTime.Parse(strHistoricalDate);
// DateTime latestPricingDate = PricingDA.GetLatestDate(symbol);
// String companyName = PricingDA.GetNameForSymbol(symbol);
// if (null == macd) macdSetup = new MACDSetup(8, 17, 9);
// else macdSetup = new MACDSetup(macd);
// historicalDate = dateGenerator.GenerateHistoricalDate(historicalDate, macdSetup.Slow);
// tradeResult.Company = companyName;
// tradeResult.MACDSetup = macdSetup;
// tradeResult.StopLossThreshold = stopLossThreshold;
// tradeResult.StopLossDays = stopLossDays;
// if (latestPricingDate < historicalDate)
// {
// MDTrace.WriteLine(LogLevel.DEBUG,"The historical date you are trying to run is later than the latest date for which prices are available.");
// return null;
// }
// tradeResult.InitialCash = initialCash;
// Dictionary<DateTime, Price> pricesByDate = new Dictionary<DateTime, Price>();
// Prices prices = PricingDA.GetPrices(symbol, DateTime.Now, historicalDate);
// Price latestPrice = prices[0];
// DateTime startDate = prices[0].Date;
// DateTime endDate = prices[prices.Count - 1].Date;
// tradeResult.StartDate = startDate;
// tradeResult.HistoricalDate = historicalDate;
// for (int index = 0; index < prices.Count; index++) pricesByDate.Add(prices[index].Date, prices[index]);
// MACDSignals macdSignals = MACDGenerator.GenerateMACD(prices, macdSetup);
// Signals signals = SignalGenerator.GenerateSignals(macdSignals);
// Portfolio portfolio = SignalTrader.TradeSignals(tradeDates, signals, prices, initialCash, stopLossThreshold, stopLossDays);
// tradeResult.AvailableCash = portfolio.AvailableCash;
// double portfolioValue = portfolio.GetPortfolioValue(latestPrice);
// double returnOnInvestment = (portfolioValue - initialCash) / initialCash;
// tradeResult.TotalPortfolioValue = portfolioValue;
// tradeResult.TotalGainLoss = portfolioValue - initialCash;
// tradeResult.ROI = returnOnInvestment;
// tradeResult.Trades = portfolio.Trades;
// tradeResult.AverageGainLoss = portfolio.GetAverageGainLoss();
// tradeResult.AverageHoldingDays = portfolio.GetAverageHoldingDays();
// tradeResult.MinimumHoldingDays = portfolio.GetMinHoldingDays();
// tradeResult.MaximumHoldingDays = portfolio.GetMaxHoldingDays();
// return tradeResult;
// }
// catch (Exception exception)
// {
// MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
// return null;
// }
// }
// }
//}

View File

@@ -0,0 +1,549 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MarketData.DataAccess;
using MarketData.Numerical;
using MarketData.MarketDataModel;
using MarketData.Utils;
namespace MarketData.Generator
{
public class ValuationGenerator
{
public delegate bool OnItemCompleteHandler(Valuation valuation);
public delegate void OnErrorItemHandler(String symbol,String message);
private ValuationGenerator()
{
}
public static Valuations GenerateValuations(List<String> symbols,OnItemCompleteHandler onItemCompleteHandler=null,OnErrorItemHandler onErrorItemHandler=null)
{
try
{
Valuations valuations = new Valuations();
valuations.ValuationDate = DateTime.Now;
Prices pricesIndex90 = PricingDA.GetPrices("SPY",90);
Prices pricesIndex720 = PricingDA.GetPrices("SPY", 720);
for (int index = 0; index < symbols.Count; index++)
{
Valuation valuation=GenerateValuation(symbols[index],pricesIndex90,pricesIndex720,onItemCompleteHandler,onErrorItemHandler);
if (null == valuation) continue;
valuations.Add(valuation);
if (null != onItemCompleteHandler)
{
if (!onItemCompleteHandler(valuation)) break;
}
}
TLBRankGenerator.ApplyTLBRanks(valuations);
AcquirersMultipeGenerator.ApplyRanks(valuations);
return valuations;
}
catch (Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
return null;
}
}
public static Valuations GenerateValuations(OnItemCompleteHandler onItemCompleteHandler=null,OnErrorItemHandler onErrorItemHandler=null)
{
try
{
List<String> symbols = PricingDA.GetSymbols();
Valuations valuations = new Valuations();
valuations.ValuationDate = DateTime.Now;
Prices pricesIndex90 = PricingDA.GetPrices("SPY",90);
Prices pricesIndex720 = PricingDA.GetPrices("SPY", 720);
for (int index = 0; index < symbols.Count; index++)
{
Valuation valuation=GenerateValuation(symbols[index],pricesIndex90,pricesIndex720,onItemCompleteHandler,onErrorItemHandler);
if (null == valuation) continue;
valuations.Add(valuation);
if (null != onItemCompleteHandler)
{
if (!onItemCompleteHandler(valuation)) break;
}
}
TLBRankGenerator.ApplyTLBRanks(valuations);
AcquirersMultipeGenerator.ApplyRanks(valuations);
return valuations;
}
catch (Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
return null;
}
}
public static Valuation GenerateValuation(String symbol,Prices pricesIndex90,Prices pricesIndex720,OnItemCompleteHandler onItemCompleteHandler=null,OnErrorItemHandler onErrorItemHandler=null)
{
try
{
Valuation valuation = new Valuation();
symbol = symbol.ToUpper();
String companyName = PricingDA.GetNameForSymbol(symbol);
Price price = PricingDA.GetPrice(symbol);
CompanyProfile companyProfile=CompanyProfileDA.GetCompanyProfile(symbol);
Prices pricesSymbol90=PricingDA.GetPrices(symbol,90);
Prices pricesSymbol720=PricingDA.GetPrices(symbol,720);
if (null == price)
{
if (null != onErrorItemHandler) onErrorItemHandler(symbol, "No Prices.");
return null;
}
IncomeStatement incomeStatement=IncomeStatementDA.GetIncomeStatement(symbol,IncomeStatement.PeriodType.Annual);
CashflowStatement cashflowStatement=CashflowStatementDA.GetCashflowStatement(symbol,CashflowStatement.PeriodType.Annual);
BalanceSheet balanceSheet = BalanceSheetDA.GetBalanceSheet(symbol,BalanceSheet.PeriodType.Annual);
TimeSeriesCollection bookValuePerShareCollection = HistoricalDA.GetTimeSeries(symbol, TimeSeriesElement.ElementType.BVPS);
TimeSeriesCollection epsCollection = HistoricalDA.GetTimeSeries(symbol, TimeSeriesElement.ElementType.EPS);
TimeSeriesCollection roicCollection = HistoricalDA.GetTimeSeries(symbol, TimeSeriesElement.ElementType.ROIC);
TimeSeriesCollection revenueCollection = HistoricalDA.GetTimeSeries(symbol, TimeSeriesElement.ElementType.Revenue);
TimeSeriesCollection freeCashflowCollection = HistoricalDA.GetTimeSeries(symbol, TimeSeriesElement.ElementType.FreeCashflow);
TimeSeriesCollection operatingCashflowCollection = HistoricalDA.GetTimeSeries(symbol, TimeSeriesElement.ElementType.OperatingCashflow);
TimeSeriesCollection workingCapitalCollection = HistoricalDA.GetTimeSeries(symbol, TimeSeriesElement.ElementType.WorkingCapital);
if (null == bookValuePerShareCollection || 0 == bookValuePerShareCollection.Count)
{
bookValuePerShareCollection=new TimeSeriesCollection();
TimeSeriesElement timeSeriesElement=new TimeSeriesElement();
timeSeriesElement.AsOf=DateTime.Now;
timeSeriesElement.Value=0;
bookValuePerShareCollection.Add(new TimeSeriesElement());
}
Fundamental fundamental = FundamentalDA.GetFundamental(symbol);
if (null == fundamental)
{
if (null != onErrorItemHandler) onErrorItemHandler(symbol, "No Fundamental Data.");
return null;
}
AnalystPriceTarget analystPriceTarget = AnalystPriceTargetDA.GetAnalystPriceTarget(symbol);
// These series are ordered by date with the latest date appearing at the lowest ordinal index.
float[] bookValuesPerShareSeries = bookValuePerShareCollection.ToFloat();
float[] epsSeries = epsCollection.ToFloat();
float[] roicSeries = roicCollection.ToFloat();
float[] revenueSeries = revenueCollection.ToFloat();
float[] freeCashflowSeries = freeCashflowCollection.ToFloat();
float[] operatingCashflowSeries = operatingCashflowCollection.ToFloat();
float[] workingCapitalSeries = workingCapitalCollection.ToFloat();
double averageGrowth = Numerics.AverageReturn(ref bookValuesPerShareSeries);
double averageGrowth2Year = Numerics.AverageReturnTop(ref bookValuesPerShareSeries, 2);
double averageGrowth4Year = Numerics.AverageReturnTop(ref bookValuesPerShareSeries, 4);
double epsGrowth = Numerics.AverageReturn(ref epsSeries);
double epsGrowth2Year = Numerics.AverageReturnTop(ref epsSeries, 2);
double epsGrowth4Year = Numerics.AverageReturnTop(ref epsSeries, 4);
double roic = Numerics.Mean(ref roicSeries);
double revenueGrowth = Numerics.AverageReturn(ref revenueSeries);
double revenueGrowth2Year = Numerics.AverageReturnTop(ref revenueSeries, 2);
double revenueGrowth4Year = Numerics.AverageReturnTop(ref revenueSeries, 4);
double freeCashflowGrowth = Numerics.AverageReturn(ref freeCashflowSeries);
double averageOperatingCashflow = Numerics.Mean(ref operatingCashflowSeries);
double averageWorkingCapital = Numerics.Mean(ref workingCapitalSeries);
double latestPrice = price.Close;
// Shares Oustanding
valuation.SharesOutstanding=fundamental.SharesOutstanding;
// Dividend Yield
valuation.DividendYield=DividendHistoryGenerator.GetDividendYield(symbol);
// Benjamin Graham Formula. EPS=Trailing Twelve month, 8.5=PE base for no growth company, g=reasonably expected 7-10 year growth rate
double intrinsicValue =GrahamGenerator.IntrinsicValue(fundamental.EPS,epsGrowth);
valuation.FundamentalValue=GrahamGenerator.GrahamNumber(fundamental.EPS,fundamental.BookValuePerShare);
if(0!=fundamental.SharesOutstanding&&!double.IsNaN(fundamental.SharesOutstanding)&&null!=balanceSheet&&!double.IsNaN(balanceSheet.NetCurrentAssetValue))valuation.NetCurrentAssetValuePerShare=balanceSheet.NetCurrentAssetValue/fundamental.SharesOutstanding;
else valuation.NetCurrentAssetValuePerShare=double.NaN;
valuation.DebtToEquity=fundamental.DebtToEquity;
// **************************************************************************************************************************************************
valuation.MarketCap=fundamental.MarketCap;
valuation.EnterpriseValue=fundamental.EnterpriseValue;
valuation.EBIT=fundamental.EBIT;
// ******************************************************* T H E L I T T L E B O O K *******************************************************
// Calculate the earnings yield to be used by TLB rankings (TLB=The Little Book)
// earnings_yield=EBIT/EnterpriseValue
if(!double.IsNaN(fundamental.EBIT)&&null!=balanceSheet&&!double.IsNaN(balanceSheet.NetFixedAssets))
{
valuation.LatestROC=fundamental.EBIT/balanceSheet.NetFixedAssets;
}
else valuation.LatestROC=double.NaN;
if(!double.IsNaN(fundamental.EBIT)&&!double.IsNaN(fundamental.EnterpriseValue)&&fundamental.EnterpriseValue>0.00&&fundamental.EBIT>0.00)
{
valuation.EarningsYield=fundamental.EBIT/fundamental.EnterpriseValue;
}
else valuation.EarningsYield=double.NaN;
// ******************************************************** T H E A C Q U I R E R S M U L T I P L E ********************************************
if(null!=fundamental&&!double.IsNaN(fundamental.EnterpriseValue)&&null!=cashflowStatement&&!double.IsNaN(cashflowStatement.DepreciationAndAmortization)&&null!=incomeStatement&&!double.IsNaN(incomeStatement.TotalRevenue)&&!double.IsNaN(incomeStatement.CostOfRevenue)&&!double.IsNaN(incomeStatement.SGA))
{
valuation.OperatingEarnings=incomeStatement.TotalRevenue-(cashflowStatement.DepreciationAndAmortization+incomeStatement.CostOfRevenue+incomeStatement.SGA);
valuation.AcquirersMultiple=fundamental.EnterpriseValue/valuation.OperatingEarnings;
}
else
{
valuation.OperatingEarnings=double.NaN;
valuation.AcquirersMultiple=double.NaN;
}
// ******************************************************************** S E C F O R M 1 3 D *********************************************************************************************
SEC13Info sec13Info=SECFilingDA.GetSEC13Info(symbol);
if(sec13Info.SEC13)
{
valuation.SEC13=true;
valuation.SEC13FilingDate=sec13Info.MostRecentFilingDate;
}
// *****************************************************************************************************************************************************************************************
valuation.Company=companyName.Replace(",", "");
valuation.Symbol=symbol;
valuation.NextEarningsDate=fundamental.NextEarningsDate;
valuation.Beta90 = BetaGenerator.Beta(symbol, PricingDA.GetLatestDate(symbol),3);
valuation.Beta720 = BetaGenerator.Beta(symbol, PricingDA.GetLatestDate(symbol),24);
if (null != roicSeries && roicSeries.Length > 0)
{
valuation.ROICDates = Utility.FormatDates(roicCollection[roicCollection.Count - 1].AsOf, roicCollection[0].AsOf);
TimeSeriesElement[] roicTimeSeriesElement=(from roicItem in roicCollection select roicItem).OrderBy(roicItem => roicItem.AsOf).ToArray();
double[] orderedROICValues=(from element in roicTimeSeriesElement select element.Value).ToArray();
valuation.ROICSlope = Numerics.Slope(orderedROICValues);
valuation.LatestROIC=roicCollection[0].Value;
}
if (null != revenueSeries && revenueSeries.Length > 0 && null != balanceSheet && !double.IsNaN(balanceSheet.LongTermDebt))
{
double latestRevenue = revenueSeries[0];
valuation.LongTermDebt=balanceSheet.LongTermDebt;
valuation.Revenue=latestRevenue;
if (latestRevenue * 3 > balanceSheet.LongTermDebt) valuation.DebtLoad="Pass";
else valuation.DebtLoad="Fail";
}
else
{
valuation.LongTermDebt=double.NaN;
valuation.Revenue=double.NaN;
valuation.DebtLoad="N/A";
}
valuation.AverageROIC=roic / 100.00;
if (null != bookValuePerShareCollection && 0 != bookValuePerShareCollection.Count)
{
valuation.BVPS = bookValuePerShareCollection[0].Value;
valuation.BVPSDates = Utility.FormatDates(bookValuePerShareCollection[bookValuePerShareCollection.Count - 1].AsOf, bookValuePerShareCollection[0].AsOf);
}
else valuation.BVPSDates = "N/A";
valuation.AverageEquityGrowth=averageGrowth;
valuation.AverageEquityGrowth2Y=averageGrowth2Year;
valuation.AverageEquityGrowth4Y=averageGrowth4Year;
if (null != epsCollection && epsCollection.Count > 0) valuation.EPSDates = Utility.FormatDates(epsCollection[epsCollection.Count - 1].AsOf, epsCollection[0].AsOf);
else valuation.EPSDates="N/A";
valuation.AverageEPSGrowth=epsGrowth;
valuation.AverageEPSGrowth2Y=epsGrowth2Year;
valuation.AverageEPSGrowth4Y=epsGrowth4Year;
if (null != revenueCollection && revenueCollection.Count > 0) valuation.RevenueDates = Utility.FormatDates(revenueCollection[revenueCollection.Count - 1].AsOf, revenueCollection[0].AsOf);
else valuation.RevenueDates="N/A";
valuation.AverageRevenueGrowth=revenueGrowth;
valuation.AverageRevenueGrowth2Y=revenueGrowth2Year;
valuation.AverageRevenueGrowth4Y=revenueGrowth4Year;
valuation.AverageFreeCashflowGrowth=freeCashflowGrowth;
valuation.AverageOperatingCashflow=averageOperatingCashflow;
valuation.OperatingCashflow=null!=operatingCashflowSeries&&operatingCashflowSeries.Length>0?operatingCashflowSeries[0]:double.NaN;
valuation.AverageWorkingCapital=averageWorkingCapital;
if(null!=companyProfile)
{
valuation.Industry=companyProfile.Industry;
valuation.Sector=companyProfile.Sector;
}
valuation.EPS=fundamental.EPS;
valuation.PE=latestPrice / fundamental.EPS;
valuation.PEG = fundamental.PEG;
if(!double.IsNaN(valuation.BVPS)&&!double.IsNaN(latestPrice))valuation.PBVPS=latestPrice/valuation.BVPS;
if (!double.IsNaN(valuation.PE) && !double.IsNaN(valuation.PEG)) valuation.ImpliedEarningsGrowth = (valuation.PE / valuation.PEG) / 100.00;
StickerPrice stickerPrice = new StickerPrice(symbol);
valuation.BVPSItems = stickerPrice.BVPSItems;
valuation.AverageBVPSGrowth = stickerPrice.AverageBVPSGrowth;
valuation.LowPE=stickerPrice.LowPE;
valuation.TrailingPE=stickerPrice.TrailingPE;
valuation.AverageLowTrailing=stickerPrice.AverageLowTrailing;
valuation.CurrentStockEstimatePrice=stickerPrice.CurrentStockEstimatePrice;
valuation.PriceEstimate10Y=stickerPrice.PriceEstimate10y;
valuation.TodaysPriceForRequiredReturn=stickerPrice.TodaysPriceForRequiredReturn;
valuation.MOS=stickerPrice.MarginOfSafety;
valuation.MOS80=stickerPrice.MarginOfSafety80Pcnt;
valuation.IntrinsicValue=intrinsicValue;
valuation.RGV=intrinsicValue/latestPrice;
valuation.LatestPrice=latestPrice;
if (null != analystPriceTarget)
{
valuation.MeanTargetPrice=analystPriceTarget.MeanTargetPrice;
valuation.LowTargetPrice=analystPriceTarget.LowTargetPrice;
valuation.HighTargetPrice=analystPriceTarget.HighTargetPrice;
}
else
{
valuation.MeanTargetPrice=double.NaN;
valuation.LowTargetPrice=double.NaN;
valuation.HighTargetPrice=double.NaN;
}
valuation.UpsidePcnt = double.IsNaN(valuation.HighTargetPrice)||0>=valuation.HighTargetPrice ? double.NaN : (valuation.HighTargetPrice - valuation.LatestPrice) / valuation.LatestPrice;
valuation.DownsidePcnt=double.IsNaN(valuation.LowTargetPrice)||0>=valuation.LowTargetPrice?double.NaN:(valuation.LowTargetPrice-valuation.LatestPrice)/valuation.LatestPrice;
valuation.Bargain=latestPrice < stickerPrice.MarginOfSafety ? true : false;
valuation.Bargain80=latestPrice < stickerPrice.MarginOfSafety80Pcnt ? true : false;
return valuation;
}
catch (Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,"Error calculating sticker for '" + symbol + "', exception was " + exception.ToString());
return null;
}
}
public static Valuation GenerateValuationWithOverride(String symbol,Prices pricesIndex90,Prices pricesIndex720,double bvpsGrowthOverride,OnItemCompleteHandler onItemCompleteHandler=null,OnErrorItemHandler onErrorItemHandler=null)
{
try
{
Valuation valuation = new Valuation();
symbol = symbol.ToUpper();
String companyName = PricingDA.GetNameForSymbol(symbol);
Price price = PricingDA.GetPrice(symbol);
CompanyProfile companyProfile=CompanyProfileDA.GetCompanyProfile(symbol);
Prices pricesSymbol90=PricingDA.GetPrices(symbol,90);
Prices pricesSymbol720=PricingDA.GetPrices(symbol,720);
if (null == price)
{
if (null != onErrorItemHandler) onErrorItemHandler(symbol, "No Prices.");
return null;
}
BalanceSheet balanceSheet = BalanceSheetDA.GetBalanceSheet(symbol,BalanceSheet.PeriodType.Annual);
IncomeStatement incomeStatement=IncomeStatementDA.GetIncomeStatement(symbol,IncomeStatement.PeriodType.Annual);
CashflowStatement cashflowStatement=CashflowStatementDA.GetCashflowStatement(symbol,CashflowStatement.PeriodType.Annual);
// These series are ordered by date with the latest date appearing at the lowest ordinal index
TimeSeriesCollection bookValuePerShareCollection = HistoricalDA.GetTimeSeries(symbol, TimeSeriesElement.ElementType.BVPS);
TimeSeriesCollection epsCollection = HistoricalDA.GetTimeSeries(symbol, TimeSeriesElement.ElementType.EPS);
TimeSeriesCollection roicCollection = HistoricalDA.GetTimeSeries(symbol, TimeSeriesElement.ElementType.ROIC);
TimeSeriesCollection revenueCollection = HistoricalDA.GetTimeSeries(symbol, TimeSeriesElement.ElementType.Revenue);
TimeSeriesCollection freeCashflowCollection = HistoricalDA.GetTimeSeries(symbol, TimeSeriesElement.ElementType.FreeCashflow);
TimeSeriesCollection operatingCashflowCollection = HistoricalDA.GetTimeSeries(symbol, TimeSeriesElement.ElementType.OperatingCashflow);
TimeSeriesCollection workingCapitalCollection = HistoricalDA.GetTimeSeries(symbol, TimeSeriesElement.ElementType.WorkingCapital);
if (null == bookValuePerShareCollection || 0 == bookValuePerShareCollection.Count)
{
bookValuePerShareCollection=new TimeSeriesCollection();
TimeSeriesElement timeSeriesElement=new TimeSeriesElement();
timeSeriesElement.AsOf=DateTime.Now;
timeSeriesElement.Value=0;
bookValuePerShareCollection.Add(new TimeSeriesElement());
}
Fundamental fundamental = FundamentalDA.GetFundamental(symbol);
if (null == fundamental)
{
if (null != onErrorItemHandler) onErrorItemHandler(symbol, "No Fundamental Data.");
return null;
}
AnalystPriceTarget analystPriceTarget = AnalystPriceTargetDA.GetAnalystPriceTarget(symbol);
float[] bookValuesPerShareSeries = bookValuePerShareCollection.ToFloat();
float[] epsSeries = epsCollection.ToFloat();
float[] roicSeries = roicCollection.ToFloat();
float[] revenueSeries = revenueCollection.ToFloat();
float[] freeCashflowSeries = freeCashflowCollection.ToFloat();
float[] operatingCashflowSeries = operatingCashflowCollection.ToFloat();
float[] workingCapitalSeries = workingCapitalCollection.ToFloat();
double averageGrowth = bvpsGrowthOverride;
double averageGrowth2Year = bvpsGrowthOverride;
double averageGrowth4Year = bvpsGrowthOverride;
double epsGrowth = Numerics.AverageReturn(ref epsSeries);
double epsGrowth2Year = Numerics.AverageReturnTop(ref epsSeries, 2);
double epsGrowth4Year = Numerics.AverageReturnTop(ref epsSeries, 4);
double roic = Numerics.Mean(ref roicSeries);
double revenueGrowth = Numerics.AverageReturn(ref revenueSeries);
double revenueGrowth2Year = Numerics.AverageReturnTop(ref revenueSeries, 2);
double revenueGrowth4Year = Numerics.AverageReturnTop(ref revenueSeries, 4);
double freeCashflowGrowth = Numerics.AverageReturn(ref freeCashflowSeries);
double averageOperatingCashflow = Numerics.Mean(ref operatingCashflowSeries);
double averageWorkingCapital = Numerics.Mean(ref workingCapitalSeries);
double latestPrice = price.Close;
// Shares Oustanding
valuation.SharesOutstanding=fundamental.SharesOutstanding;
// Dividend Yield
valuation.DividendYield=DividendHistoryGenerator.GetDividendYield(symbol);
// Benjamin Graham Formula. EPS=Trailing Twelve month, 8.5=PE base for no growth company, g=reasonably expected 7-10 year growth rate
double intrinsicValue = GrahamGenerator.IntrinsicValue(fundamental.EPS,epsGrowth);
valuation.FundamentalValue=GrahamGenerator.GrahamNumber(fundamental.EPS,fundamental.BookValuePerShare);
if(0!=fundamental.SharesOutstanding&&!double.IsNaN(fundamental.SharesOutstanding)&&null!=balanceSheet&&!double.IsNaN(balanceSheet.NetCurrentAssetValue))valuation.NetCurrentAssetValuePerShare=balanceSheet.NetCurrentAssetValue/fundamental.SharesOutstanding;
else valuation.NetCurrentAssetValuePerShare=double.NaN;
valuation.DebtToEquity=fundamental.DebtToEquity;
// **************************************************************************************************************************************************
valuation.MarketCap=fundamental.MarketCap;
valuation.EnterpriseValue=fundamental.EnterpriseValue;
valuation.EBIT=fundamental.EBIT;
// ******************************************************* T H E L I T T L E B O O K *******************************************************
// Calculate the earnings yield to be used by TLB rankings (TLB=The Little Book)
// earnings_yield=EBIT/EnterpriseValue
if(!double.IsNaN(fundamental.EBIT)&&!double.IsNaN(fundamental.EnterpriseValue)&&fundamental.EnterpriseValue>0.00&&fundamental.EBIT>0.00)
{
valuation.EarningsYield=fundamental.EBIT/fundamental.EnterpriseValue;
}
else valuation.EarningsYield=double.NaN;
// ******************************************************** T H E A C Q U I R E R S M U L T I P L E ********************************************
if(null!=fundamental&&!double.IsNaN(fundamental.EnterpriseValue)&&null!=cashflowStatement&&!double.IsNaN(cashflowStatement.DepreciationAndAmortization)&&null!=incomeStatement&&!double.IsNaN(incomeStatement.TotalRevenue)&&!double.IsNaN(incomeStatement.CostOfRevenue)&&!double.IsNaN(incomeStatement.SGA))
{
valuation.OperatingEarnings=incomeStatement.TotalRevenue-(cashflowStatement.DepreciationAndAmortization+incomeStatement.CostOfRevenue+incomeStatement.SGA);
valuation.AcquirersMultiple=fundamental.EnterpriseValue/valuation.OperatingEarnings;
}
else
{
valuation.OperatingEarnings=double.NaN;
valuation.AcquirersMultiple=double.NaN;
}
// ******************************************************************** S E C F O R M 1 3 D *********************************************************************************************
SEC13Info sec13Info=SECFilingDA.GetSEC13Info(symbol);
if(sec13Info.SEC13)
{
valuation.SEC13=true;
valuation.SEC13FilingDate=sec13Info.MostRecentFilingDate;
}
// *****************************************************************************************************************************************************************************************
if(null!=companyProfile)
{
valuation.Industry=companyProfile.Industry;
valuation.Sector=companyProfile.Sector;
}
valuation.Company=companyName.Replace(",", "");
valuation.Symbol=symbol;
valuation.NextEarningsDate=fundamental.NextEarningsDate;
valuation.Beta90 = BetaGenerator.Beta(symbol, PricingDA.GetLatestDate(symbol), 3);
valuation.Beta720 = BetaGenerator.Beta(symbol, PricingDA.GetLatestDate(symbol), 24);
if (null != roicSeries && roicSeries.Length > 0)
{
valuation.ROICDates = Utility.FormatDates(roicCollection[roicCollection.Count - 1].AsOf, roicCollection[0].AsOf);
TimeSeriesElement[] roicTimeSeriesElement=(from roicItem in roicCollection select roicItem).OrderBy(roicItem => roicItem.AsOf).ToArray();
double[] orderedROICValues=(from element in roicTimeSeriesElement select element.Value).ToArray();
valuation.ROICSlope = Numerics.Slope(orderedROICValues);
valuation.LatestROIC=roicCollection[0].Value;
}
if (null != revenueSeries && revenueSeries.Length > 0 && null != balanceSheet && !double.IsNaN(balanceSheet.LongTermDebt))
{
double latestRevenue = revenueSeries[0];
valuation.LongTermDebt=balanceSheet.LongTermDebt;
valuation.Revenue=latestRevenue;
if (latestRevenue * 3 > balanceSheet.LongTermDebt) valuation.DebtLoad="Pass";
else valuation.DebtLoad="Fail";
}
else
{
valuation.LongTermDebt=double.NaN;
valuation.Revenue=double.NaN;
valuation.DebtLoad="N/A";
}
valuation.AverageROIC=roic / 100.00;
if (null != bookValuePerShareCollection && 0 != bookValuePerShareCollection.Count)
{
valuation.BVPS = bookValuePerShareCollection[0].Value;
valuation.BVPSDates = Utility.FormatDates(bookValuePerShareCollection[bookValuePerShareCollection.Count - 1].AsOf, bookValuePerShareCollection[0].AsOf);
}
else valuation.BVPSDates = "N/A";
valuation.AverageEquityGrowth=averageGrowth;
valuation.AverageEquityGrowth2Y=averageGrowth2Year;
valuation.AverageEquityGrowth4Y=averageGrowth4Year;
if (null != epsCollection && epsCollection.Count > 0) valuation.EPSDates = Utility.FormatDates(epsCollection[epsCollection.Count - 1].AsOf, epsCollection[0].AsOf);
else valuation.EPSDates="N/A";
valuation.AverageEPSGrowth=epsGrowth;
valuation.AverageEPSGrowth2Y=epsGrowth2Year;
valuation.AverageEPSGrowth4Y=epsGrowth4Year;
if (null != revenueCollection && revenueCollection.Count > 0) valuation.RevenueDates = Utility.FormatDates(revenueCollection[revenueCollection.Count - 1].AsOf, revenueCollection[0].AsOf);
else valuation.RevenueDates="N/A";
valuation.AverageRevenueGrowth=revenueGrowth;
// if (null != fundamental) valuation.AverageRevenueGrowthQtr=fundamental.QtrlyRevenueGrowth / 100.00;
// else valuation.AverageRevenueGrowthQtr=double.NaN;
valuation.AverageRevenueGrowth2Y=revenueGrowth2Year;
valuation.AverageRevenueGrowth4Y=revenueGrowth4Year;
valuation.AverageFreeCashflowGrowth=freeCashflowGrowth;
valuation.AverageOperatingCashflow=averageOperatingCashflow;
valuation.OperatingCashflow=null!=operatingCashflowSeries&&operatingCashflowSeries.Length>0?operatingCashflowSeries[0]:double.NaN;
valuation.AverageWorkingCapital=averageWorkingCapital;
valuation.EPS=fundamental.EPS;
valuation.PE=latestPrice / fundamental.EPS;
valuation.PEG = fundamental.PEG;
if(!double.IsNaN(valuation.BVPS)&&!double.IsNaN(latestPrice))valuation.PBVPS=latestPrice/valuation.BVPS;
if (!double.IsNaN(valuation.PE) && !double.IsNaN(valuation.PEG)) valuation.ImpliedEarningsGrowth = (valuation.PE / valuation.PEG) / 100.00;
StickerPrice stickerPrice = new StickerPrice(symbol,bvpsGrowthOverride);
valuation.BVPSItems = stickerPrice.BVPSItems;
valuation.AverageBVPSGrowth = stickerPrice.AverageBVPSGrowth;
valuation.LowPE=stickerPrice.LowPE;
valuation.TrailingPE=stickerPrice.TrailingPE;
valuation.AverageLowTrailing=stickerPrice.AverageLowTrailing;
valuation.CurrentStockEstimatePrice=stickerPrice.CurrentStockEstimatePrice;
valuation.PriceEstimate10Y=stickerPrice.PriceEstimate10y;
valuation.TodaysPriceForRequiredReturn=stickerPrice.TodaysPriceForRequiredReturn;
valuation.MOS=stickerPrice.MarginOfSafety;
valuation.MOS80=stickerPrice.MarginOfSafety80Pcnt;
valuation.IntrinsicValue=intrinsicValue;
valuation.RGV=intrinsicValue/latestPrice;
valuation.LatestPrice=latestPrice;
if (null != analystPriceTarget)
{
valuation.MeanTargetPrice=analystPriceTarget.MeanTargetPrice;
valuation.LowTargetPrice=analystPriceTarget.LowTargetPrice;
valuation.HighTargetPrice=analystPriceTarget.HighTargetPrice;
}
else
{
valuation.MeanTargetPrice=double.NaN;
valuation.LowTargetPrice=double.NaN;
valuation.HighTargetPrice=double.NaN;
}
valuation.UpsidePcnt = double.IsNaN(valuation.HighTargetPrice)||0>=valuation.HighTargetPrice ? double.NaN : (valuation.HighTargetPrice - valuation.LatestPrice) / valuation.LatestPrice;
valuation.DownsidePcnt=double.IsNaN(valuation.LowTargetPrice)||0>=valuation.LowTargetPrice?double.NaN:(valuation.LowTargetPrice-valuation.LatestPrice)/valuation.LatestPrice;
valuation.Bargain=latestPrice < stickerPrice.MarginOfSafety ? true : false;
valuation.Bargain80=latestPrice < stickerPrice.MarginOfSafety80Pcnt ? true : false;
return valuation;
}
catch (Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,"Error calculating sticker for '" + symbol + "', exception was " + exception.ToString());
return null;
}
}
public static String ToString(Valuation valuation)
{
StringBuilder sb = new StringBuilder();
sb.Append(valuation.Company).Append(",");
sb.Append(valuation.Symbol).Append(",");
sb.Append(Utility.DateTimeToStringMMHDDHYYYY(valuation.NextEarningsDate)).Append(",");
sb.Append(Utility.FormatNumber(valuation.LongTermDebt)).Append(",");
sb.Append(Utility.FormatNumber(valuation.Revenue)).Append(",");
sb.Append(valuation.DebtLoad).Append(",");
sb.Append(Utility.FormatNumber(valuation.AverageROIC)).Append(",");
sb.Append(valuation.BVPSDates).Append(",");
sb.Append(Utility.FormatNumber(valuation.AverageEquityGrowth)).Append(",");
sb.Append(Utility.FormatNumber(valuation.AverageEquityGrowth2Y)).Append(",");
sb.Append(Utility.FormatNumber(valuation.AverageEquityGrowth4Y)).Append(",");
sb.Append(valuation.EPSDates).Append(",");
sb.Append(Utility.FormatNumber(valuation.AverageEPSGrowth)).Append(",");
sb.Append(Utility.FormatNumber(valuation.AverageEPSGrowth2Y)).Append(",");
sb.Append(Utility.FormatNumber(valuation.AverageEPSGrowth4Y)).Append(",");
sb.Append(valuation.RevenueDates).Append(",");
sb.Append(Utility.FormatNumber(valuation.AverageRevenueGrowth)).Append(",");
// sb.Append(valuation.AverageRevenueGrowthQtr).Append(",");
sb.Append(Utility.FormatNumber(valuation.AverageRevenueGrowth2Y)).Append(",");
sb.Append(Utility.FormatNumber(valuation.AverageRevenueGrowth4Y)).Append(",");
sb.Append(Utility.FormatNumber(valuation.AverageFreeCashflowGrowth)).Append(",");
sb.Append(Utility.FormatNumber(valuation.AverageOperatingCashflow)).Append(",");
sb.Append(Utility.FormatNumber(valuation.AverageWorkingCapital)).Append(",");
sb.Append(Utility.FormatNumber(valuation.BVPS)).Append(",");
sb.Append(Utility.FormatNumber(valuation.EPS)).Append(",");
sb.Append(Utility.FormatNumber(valuation.PE)).Append(",");
sb.Append(Utility.FormatNumber(valuation.PEG)).Append(",");
sb.Append(Utility.FormatNumber(valuation.ImpliedEarningsGrowth)).Append(",");
sb.Append(Utility.FormatNumber(valuation.LowPE)).Append(",");
sb.Append(Utility.FormatNumber(valuation.TrailingPE)).Append(",");
sb.Append(Utility.FormatNumber(valuation.AverageLowTrailing)).Append(",");
sb.Append(Utility.FormatNumber(valuation.CurrentStockEstimatePrice)).Append(",");
sb.Append(Utility.FormatNumber(valuation.PriceEstimate10Y)).Append(",");
sb.Append(Utility.FormatNumber(valuation.TodaysPriceForRequiredReturn)).Append(",");
sb.Append(Utility.FormatNumber(valuation.MOS)).Append(",");
sb.Append(Utility.FormatNumber(valuation.MOS80)).Append(",");
sb.Append(Utility.FormatNumber(valuation.IntrinsicValue)).Append(",");
sb.Append(Utility.FormatNumber(valuation.RGV)).Append(",");
sb.Append(Utility.FormatNumber(valuation.LatestPrice)).Append(",");
sb.Append(Utility.FormatNumber(valuation.UpsidePcnt)).Append(",");
sb.Append(Utility.FormatNumber(valuation.DownsidePcnt)).Append(",");
sb.Append(Utility.FormatNumber(valuation.MeanTargetPrice)).Append(",");
sb.Append(Utility.FormatNumber(valuation.LowTargetPrice)).Append(",");
sb.Append(Utility.FormatNumber(valuation.HighTargetPrice)).Append(",");
sb.Append(valuation.Bargain ? true : false).Append(",");
sb.Append(valuation.Bargain80 ? true : false).Append(",");
sb.Append(Utility.FormatNumber(valuation.DividendYield));
return sb.ToString();
}
public static String Heading()
{
StringBuilder sb = new StringBuilder();
sb.Append("Company,Symbol,NextEarningDate,LongTermDebt,Revenue,DebtLoad,ROIC(Avg%),BVPSDates,AvgEquityGrowth(AvgBVPS%),AvgEquityGrowth_2Y(AvgBVPS%),AvgEquityGrowth_4Y(AvgBVPS%),EPSDates,EPSGrowth(Avg%),EPSGrowth_2Y(Avg%),EPSGrowth_4Y(Avg%),RevenueDates,RevenueGrowth(Avg%),RevenueGrowth_QTR(Avg%),RevenueGrowth_2Y(Avg%),RevenueGrowth_4Y(Avg%),FreeCashflowGrowth(Avg%),OperatingCashflow(Avg),WorkingCapital(Avg),BVPS,EPS,PE,PEG,ImpliedEarningsGrowth,LowPE,TrailingPE,AverageLowTrailing,CurrentStockEstimatePrice,PriceEstimate10y,TodaysPriceForRequiredReturn,MOS,MOS(80%),IntrinsicValue,RGV(sb>1),LatestPrice,UpsidePcnt,DownsidePcnt,MeanTargetPrice,LowTargetPrice,HighPrice,Bargain(MOS),Bargain(MOS80%),DividendYield");
return sb.ToString();
}
}
}