Improve the BetaGenerator
This commit is contained in:
@@ -1,14 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using MarketData.Cache;
|
||||
using MarketData.DataAccess;
|
||||
using MarketData.MarketDataModel;
|
||||
using MarketData.Utils;
|
||||
|
||||
// This Beta calculator is modelled after Yahoo Finance Beta calculator. It calculates Beta using 36 monthly prices start at beginning of previous month and going back 36 months
|
||||
// While the calculator does not match exactly to Yahoo Finance I will use this as an alternative in the event that Yahoo Finance Beta is no longer available.
|
||||
// This Beta calculator is modelled after Yahoo Finance Beta calculator. It calculates Beta using 36 (default) monthly prices starting at beginning of previous month
|
||||
// and going back 36 months. While the calculator does not match exactly to Yahoo Finance I will use this as an alternative in the event that Yahoo Finance Beta is
|
||||
// no longer available.
|
||||
namespace MarketData.Numerical
|
||||
{
|
||||
public class BetaPrices : List<BetaPrice>
|
||||
@@ -16,7 +15,8 @@ namespace MarketData.Numerical
|
||||
public BetaPrices()
|
||||
{
|
||||
}
|
||||
// assuming that the list is in descending date order
|
||||
|
||||
// Assumes that the list is in descending date order with the Latest (Most Recent) date in the lowest index and Earliest (Most Historical) date in the highest index
|
||||
public double[] ReturnsBenchmark()
|
||||
{
|
||||
double[] returns=new double[Count-1];
|
||||
@@ -27,6 +27,8 @@ namespace MarketData.Numerical
|
||||
}
|
||||
return returns;
|
||||
}
|
||||
|
||||
// Assumes that the list is in descending date order with the Latest (Most Recent) date in the lowest index and Earliest (Most Historical) date in the highest index
|
||||
public double[] ReturnsSymbol()
|
||||
{
|
||||
double[] returns=new double[Count-1];
|
||||
@@ -38,6 +40,7 @@ namespace MarketData.Numerical
|
||||
return returns;
|
||||
}
|
||||
}
|
||||
|
||||
public class BetaPrice
|
||||
{
|
||||
public BetaPrice(String symbol,String benchmark,Price symbolPrice,Price benchmarkPrice,DateTime pricingDate)
|
||||
@@ -60,11 +63,13 @@ namespace MarketData.Numerical
|
||||
private BetaGenerator()
|
||||
{
|
||||
}
|
||||
public static double Beta(String symbol,int months = 36)
|
||||
public static double Beta(String symbol,int months = 36, bool verbose=true)
|
||||
{
|
||||
return Beta(symbol, PricingDA.GetLatestDate(symbol), months);
|
||||
return Beta(symbol, PricingDA.GetLatestDate(symbol), months, verbose);
|
||||
}
|
||||
public static double Beta(String symbol,DateTime asof,int months=36)
|
||||
|
||||
|
||||
public static double Beta(String symbol,DateTime asof,int months=36,bool verbose=true)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -77,28 +82,38 @@ namespace MarketData.Numerical
|
||||
Dictionary<DateTime, Price> symbolPricesByDate = new Dictionary<DateTime, Price>();
|
||||
Dictionary<DateTime, Price> benchmarkPricesByDate = new Dictionary<DateTime, Price>();
|
||||
List<DateTime> historicalDates = new List<DateTime>();
|
||||
|
||||
while (historicalDates.Count < (months + 1))
|
||||
{
|
||||
historicalDates.Add(startDate);
|
||||
startDate = dateGenerator.GetPrevMonthStart(startDate);
|
||||
}
|
||||
Prices symbolPrices = PricingDA.GetPrices(symbol, asof, historicalDates[historicalDates.Count - 1]);
|
||||
Prices benchmarkPrices = PricingDA.GetPrices(benchmark, asof, historicalDates[historicalDates.Count - 1]);
|
||||
DateTime earliestDate = historicalDates[historicalDates.Count - 1];
|
||||
|
||||
Prices symbolPrices = GBPriceCache.GetInstance().GetPrices(symbol, asof, earliestDate);
|
||||
Prices benchmarkPrices = GBPriceCache.GetInstance().GetPrices(benchmark, asof, earliestDate);
|
||||
|
||||
foreach (Price price in symbolPrices) symbolPricesByDate.Add(price.Date, price);
|
||||
foreach (Price price in benchmarkPrices) benchmarkPricesByDate.Add(price.Date, price);
|
||||
|
||||
startDate = dateGenerator.GetPrevMonthStart(asof);
|
||||
|
||||
while (betaPrices.Count < (months + 1))
|
||||
{
|
||||
BetaPrice betaPrice = GetPrice(symbol, benchmark, startDate, symbolPricesByDate, benchmarkPricesByDate);
|
||||
if (null == betaPrice)
|
||||
{
|
||||
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Beta({0},{1}).. Cannot calculate Beta. Missing price on {2}",symbol,Utility.DateTimeToStringMMHDDHYYYY(asof),Utility.DateTimeToStringMMHDDHYYYY(startDate)));
|
||||
if(verbose)
|
||||
{
|
||||
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("INFO:Beta({0},{1}).. Cannot calculate Beta. Missing price on {2}",symbol,Utility.DateTimeToStringMMHDDHYYYY(asof),Utility.DateTimeToStringMMHDDHYYYY(startDate)));
|
||||
}
|
||||
return double.NaN;
|
||||
}
|
||||
betaPrices.Add(betaPrice);
|
||||
startDate = dateGenerator.GetPrevMonthStart(startDate);
|
||||
if (startDate < minPricingDate) break;
|
||||
}
|
||||
|
||||
double[] returnsSymbol = betaPrices.ReturnsSymbol();
|
||||
double[] returnsBenchmark = betaPrices.ReturnsBenchmark();
|
||||
double beta = Numerics.Beta(ref returnsSymbol, ref returnsBenchmark);
|
||||
@@ -110,23 +125,33 @@ namespace MarketData.Numerical
|
||||
return double.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Fetch the first price of the month for both the asset and the benchmark, allowing both to walk forward in the month by up to 10 days until we get a good price
|
||||
/// </summary>
|
||||
/// <param name="symbol"></param>
|
||||
/// <param name="benchmark"></param>
|
||||
/// <param name="requestedDate"></param>
|
||||
/// <param name="symbolPricesByDate"></param>
|
||||
/// <param name="benchmarkPricesByDate"></param>
|
||||
/// <returns></returns>
|
||||
private static BetaPrice GetPrice(String symbol, String benchmark, DateTime requestedDate, Dictionary<DateTime, Price> symbolPricesByDate, Dictionary<DateTime, Price> benchmarkPricesByDate)
|
||||
{
|
||||
try
|
||||
{
|
||||
int maxAdvanceDays = 10;
|
||||
Price symbolPrice = null;
|
||||
Price benchmarkPrice = null;
|
||||
for (int advanceDays = 0; advanceDays < maxAdvanceDays; advanceDays++)
|
||||
{
|
||||
if (!symbolPricesByDate.ContainsKey(requestedDate)) { requestedDate = requestedDate.AddDays(1); continue; }
|
||||
symbolPrice = symbolPricesByDate[requestedDate];
|
||||
if (!benchmarkPricesByDate.ContainsKey(requestedDate)) { requestedDate = requestedDate.AddDays(1); continue; }
|
||||
benchmarkPrice = benchmarkPricesByDate[requestedDate];
|
||||
}
|
||||
Price symbolPrice = default;
|
||||
Price benchmarkPrice = default;
|
||||
|
||||
symbolPrice = GetPriceWithinDays(requestedDate, maxAdvanceDays, symbolPricesByDate);
|
||||
benchmarkPrice = GetPriceWithinDays(requestedDate, maxAdvanceDays, benchmarkPricesByDate);
|
||||
|
||||
if (null == symbolPrice || null == benchmarkPrice) return null;
|
||||
|
||||
symbolPrice.Date = requestedDate.Date;
|
||||
benchmarkPrice.Date = requestedDate.Date;
|
||||
|
||||
return new BetaPrice(symbol, benchmark, symbolPrice, benchmarkPrice, requestedDate);
|
||||
}
|
||||
catch (Exception exception)
|
||||
@@ -135,6 +160,130 @@ namespace MarketData.Numerical
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Price GetPriceWithinDays(DateTime requestedDate, int maxDays, Dictionary<DateTime,Price> pricesByDate)
|
||||
{
|
||||
DateGenerator dateGenerator = new DateGenerator();
|
||||
|
||||
List<DateTime> futureDates = dateGenerator.GenerateFutureDates(requestedDate, maxDays);
|
||||
foreach(DateTime futureDate in futureDates)
|
||||
{
|
||||
if(pricesByDate.ContainsKey(futureDate))
|
||||
{
|
||||
return pricesByDate[futureDate];
|
||||
}
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
// assuming that the list is in descending date order
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Fetch the first price of the month for both the asset and the benchmark, allowing both to walk forward in the month by up to 10 days until we get a good price
|
||||
/// </summary>
|
||||
/// <param name="symbol"></param>
|
||||
/// <param name="benchmark"></param>
|
||||
/// <param name="requestedDate"></param>
|
||||
/// <param name="symbolPricesByDate"></param>
|
||||
/// <param name="benchmarkPricesByDate"></param>
|
||||
/// <returns></returns>
|
||||
//private static BetaPrice GetPrice(String symbol, String benchmark, DateTime requestedDate, Dictionary<DateTime, Price> symbolPricesByDate, Dictionary<DateTime, Price> benchmarkPricesByDate)
|
||||
//{
|
||||
// try
|
||||
// {
|
||||
// int maxAdvanceDays = 10;
|
||||
// Price symbolPrice = default;
|
||||
// Price benchmarkPrice = default;
|
||||
|
||||
// for (int advanceDays = 0; advanceDays < maxAdvanceDays; advanceDays++)
|
||||
// {
|
||||
// if (!symbolPricesByDate.ContainsKey(requestedDate))
|
||||
// {
|
||||
// requestedDate = requestedDate.AddDays(1);
|
||||
// continue;
|
||||
// }
|
||||
// symbolPrice = symbolPricesByDate[requestedDate];
|
||||
|
||||
// if (!benchmarkPricesByDate.ContainsKey(requestedDate))
|
||||
// {
|
||||
// requestedDate = requestedDate.AddDays(1);
|
||||
// continue;
|
||||
// }
|
||||
// benchmarkPrice = benchmarkPricesByDate[requestedDate];
|
||||
// if(null != symbolPrice && null != benchmarkPrice)break;
|
||||
// }
|
||||
|
||||
// if (null == symbolPrice || null == benchmarkPrice) return null;
|
||||
|
||||
// symbolPrice.Date = requestedDate.Date;
|
||||
// benchmarkPrice.Date = requestedDate.Date;
|
||||
// return new BetaPrice(symbol, benchmark, symbolPrice, benchmarkPrice, requestedDate);
|
||||
// }
|
||||
// catch (Exception exception)
|
||||
// {
|
||||
// MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Exception:{0}", exception.ToString()));
|
||||
// return null;
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
//public static double Beta(String symbol,DateTime asof,int months=36)
|
||||
//{
|
||||
// try
|
||||
// {
|
||||
// String benchmark = "SPY";
|
||||
// DateGenerator dateGenerator = new DateGenerator();
|
||||
|
||||
// BetaPrices betaPrices = new BetaPrices();
|
||||
// DateTime startDate = dateGenerator.GetPrevMonthStart(asof);
|
||||
// DateTime minPricingDate = PricingDA.GetEarliestDate(symbol);
|
||||
// Dictionary<DateTime, Price> symbolPricesByDate = new Dictionary<DateTime, Price>();
|
||||
// Dictionary<DateTime, Price> benchmarkPricesByDate = new Dictionary<DateTime, Price>();
|
||||
// List<DateTime> historicalDates = new List<DateTime>();
|
||||
|
||||
// while (historicalDates.Count < (months + 1))
|
||||
// {
|
||||
// historicalDates.Add(startDate);
|
||||
// startDate = dateGenerator.GetPrevMonthStart(startDate);
|
||||
// }
|
||||
// DateTime earliestDate = historicalDates[historicalDates.Count - 1];
|
||||
|
||||
// Prices symbolPrices = PricingDA.GetPrices(symbol, asof, earliestDate);
|
||||
// Prices benchmarkPrices = PricingDA.GetPrices(benchmark, asof, earliestDate);
|
||||
|
||||
// foreach (Price price in symbolPrices) symbolPricesByDate.Add(price.Date, price);
|
||||
// foreach (Price price in benchmarkPrices) benchmarkPricesByDate.Add(price.Date, price);
|
||||
|
||||
// startDate = dateGenerator.GetPrevMonthStart(asof);
|
||||
|
||||
// while (betaPrices.Count < (months + 1))
|
||||
// {
|
||||
// BetaPrice betaPrice = GetPrice(symbol, benchmark, startDate, symbolPricesByDate, benchmarkPricesByDate);
|
||||
// if (null == betaPrice)
|
||||
// {
|
||||
// MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Beta({0},{1}).. Cannot calculate Beta. Missing price on {2}",symbol,Utility.DateTimeToStringMMHDDHYYYY(asof),Utility.DateTimeToStringMMHDDHYYYY(startDate)));
|
||||
// return double.NaN;
|
||||
// }
|
||||
// betaPrices.Add(betaPrice);
|
||||
// startDate = dateGenerator.GetPrevMonthStart(startDate);
|
||||
// if (startDate < minPricingDate) break;
|
||||
// }
|
||||
|
||||
// double[] returnsSymbol = betaPrices.ReturnsSymbol();
|
||||
// double[] returnsBenchmark = betaPrices.ReturnsBenchmark();
|
||||
// double beta = Numerics.Beta(ref returnsSymbol, ref returnsBenchmark);
|
||||
// return beta;
|
||||
// }
|
||||
// catch (Exception exception)
|
||||
// {
|
||||
// MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Exception:{0}",exception.ToString()));
|
||||
// return double.NaN;
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user