Files
marketdata/Scraps/Backtest.cs.txt
2024-02-22 14:52:53 -05:00

687 lines
39 KiB
Plaintext

using System;
using System.Collections.Generic;
using System.Text;
using MarketData.MarketDataModel;
using MarketData.DataAccess;
using MarketData.Utils;
using System.Linq;
using MarketData.Helper;
using MarketData.Numerical;
using System.Threading;
using MarketData.Integration;
namespace MarketData.Generator.Momentum
{
public class MomentumBacktest
{
private double CashBalance{get;set;}
private MGConfiguration Configuration{get;set;}
private int HoldingPeriod{get{return Configuration.HoldingPeriod;}}
private int MaxPositions{get{return Configuration.MaxPositions;}}
private List<String> NoTradeSymbols{get{return Utility.ToList(Configuration.NoTradeSymbols);}}
private ActivePositions ActivePositions{get;set;}
private SMSNotifications SMSNotifications{get;set;}
private Positions AllPositions{get;set;}
private int Cycle{get;set;}
private DateTime TradeDate{get;set;}
private DateTime StartDate{get;set;}
private DateTime AnalysisDate{get;set;}
private String PathSessionFileName{get;set;}
// ******************************************************************************************************************************************************
//********************************************************** U P D A T E S E S S I O N P R I C E *****************************************************
// ******************************************************************************************************************************************************
public void UpdateSessionPrice(String symbol,DateTime tradeDate,double price,String pathSessionFileName)
{
MGSessionParams sessionParams=null;
if(null==symbol||Utility.IsEpoch(tradeDate)||null==pathSessionFileName)
{
MDTrace.WriteLine(LogLevel.DEBUG,"UpdateSessionPrice. One or more parameters are invalid.");
return;
}
PathSessionFileName=pathSessionFileName;
if(null==(sessionParams=RestoreSession()))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Error loading session file {0}",pathSessionFileName));
return;
}
List<int> keys=new List<int>(ActivePositions.Keys);
bool hasChanges=false;
foreach(int key in keys)
{
Positions positions=ActivePositions[key];
foreach(Position position in positions)
{
if(!position.Symbol.Equals(symbol)||!position.PurchaseDate.Date.Equals(tradeDate.Date))continue;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Changing purchase price for {0} on {1} from {2} to {3}",
position.Symbol,
Utility.DateTimeToStringMMHDDHYYYY(position.PurchaseDate),
Utility.FormatCurrency(position.PurchasePrice),
Utility.FormatCurrency(price)));
position.PurchasePrice=price;
if(!hasChanges)hasChanges=true;
}
}
if(hasChanges)SaveSession();
ActivePositions.Display();
DisplayBalanceFromPositions();
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("StartDate:{0}",Utility.DateTimeToStringMMHDDHYYYY(StartDate)));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TradeDate:{0}",Utility.DateTimeToStringMMHDDHYYYY(TradeDate)));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("AnalysisDate:{0}",Utility.DateTimeToStringMMHDDHYYYY(AnalysisDate)));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Next Slot:{0}",Cycle));
}
// ******************************************************************************************************************************************************
//************************************************************** D I S P L A Y S E S S I O N *****************************************************
// ******************************************************************************************************************************************************
public void DisplaySession(String paramPathSessionFileName)
{
if(null==paramPathSessionFileName)return;
PathSessionFileName=paramPathSessionFileName;
MGSessionParams sessionParams=null;
if(null==(sessionParams=RestoreSession()))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Error loading session file {0}",paramPathSessionFileName));
return;
}
Console.WriteLine(String.Format("SessionFile:{0} Last Updated:{1}",paramPathSessionFileName,sessionParams.LastUpdated));
Configuration.DisplayConfiguration();
MDTrace.WriteLine(LogLevel.DEBUG,"************** A L L P O S I T I O N S *************");
AllPositions=new Positions((from Position position in AllPositions orderby position.PurchaseDate ascending select position).ToList());
AllPositions.Display();
MDTrace.WriteLine(LogLevel.DEBUG,"************** A C T I V E P O S I T I O N S *************");
ActivePositions.Display();
DisplayBalanceFromPositions();
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("StartDate:{0}",Utility.DateTimeToStringMMHDDHYYYY(StartDate)));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TradeDate:{0}",Utility.DateTimeToStringMMHDDHYYYY(TradeDate)));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("AnalysisDate:{0}",Utility.DateTimeToStringMMHDDHYYYY(AnalysisDate)));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Next Slot:{0}",Cycle));
}
// ******************************************************************************************************************************************************
//******************************************************* L I Q U I D A T E A L L P O S I T I O N S ***********************************************
// ******************************************************************************************************************************************************
public void MGLiquididate(String pathSessionFile,DateTime? tradeDate)
{
if (null == pathSessionFile) return;
MGSessionParams sessionParams=null;
PathSessionFileName = pathSessionFile;
if (null == (sessionParams = RestoreSession()))
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Error loading session file {0}", pathSessionFile));
return;
}
MDTrace.WriteLine(LogLevel.DEBUG, "************** L I Q U I D A T E P O S I T I O N S *************");
if (null == ActivePositions || 0 == ActivePositions.Count)
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("No active positions in file {0}", pathSessionFile));
return;
}
if(null==tradeDate)tradeDate = PricingDA.GetLatestDate(ActivePositions.GetSymbols());
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Trade date:{0}", Utility.DateTimeToStringMMHDDHYYYY(tradeDate.Value)));
if(null==(sessionParams=RestoreSession()))
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Error loading session file {0}", pathSessionFile));
return;
}
for (int slotIndex = 0; slotIndex < ActivePositions.Count; slotIndex++)
{
Positions slotPositions = ActivePositions[slotIndex];
SellPositions(slotPositions, tradeDate.Value);
MDTrace.WriteLine(LogLevel.DEBUG, "********************* S E L L *********************");
slotPositions.Display();
AllPositions.Add(slotPositions);
CashBalance += slotPositions.MarketValue;
ActivePositions[slotIndex].Clear();
}
MGPriceCache.GetInstance().Dispose();
SaveSession();
}
// ******************************************************************************************************************************************************
// ****************************************************************** B A C K T E S T *****************************************************************
// ******************************************************************************************************************************************************
// Ideally, startDate should be November,February,May,August
// paramStartDate is startDate {ORIGINAL START DATE}
// paramAnalysisDate is endDate {TODAY}
public BacktestResult PerformBacktest(DateTime paramStartDate,DateTime paramAnalysisDate,String paramPathSessionFileName,MGConfiguration configuration)
{
BacktestResult backTestResult=new BacktestResult();
DateGenerator dateGenerator=new DateGenerator();
Configuration=configuration;
CashBalance=Configuration.InitialCash;
ActivePositions=new ActivePositions();
AllPositions=new Positions();
StartDate=paramStartDate;
TradeDate=paramStartDate;
AnalysisDate=paramAnalysisDate;
PathSessionFileName=paramPathSessionFileName;
SMSNotifications=new SMSNotifications();
MGSessionParams sessionParams=null;
Cycle=0;
if(AnalysisDate.Date>Today().Date)return backTestResult;
if(Utility.IsEpoch(AnalysisDate))AnalysisDate=Today();
TradeDate=dateGenerator.GetCurrentMonthEnd(StartDate);
if(TradeDate>AnalysisDate)
{
int startMonth=StartDate.Month;
TimeSpan timeSpan=new TimeSpan();
if((new int[]{12,3,6,9}).Any(x=>x.Equals(startMonth)))timeSpan=new TimeSpan(30,0,0,0);
else if((new int[]{1,4,7,10}).Any(x=>x.Equals(startMonth)))timeSpan=new TimeSpan(60,0,0,0);
else if((new int[]{2,5,8,11}).Any(x=>x.Equals(startMonth)))timeSpan=new TimeSpan(90,0,0,0);
StartDate=StartDate-timeSpan;
TradeDate=dateGenerator.GetCurrentMonthEnd(StartDate);
}
if(null!=PathSessionFileName)sessionParams=RestoreSession();
if(null!=sessionParams)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Using session file {0}, Last updated {1}",paramPathSessionFileName,sessionParams.LastUpdated));
}
DisplayBalance();
while(true)
{
if(TradeDate>AnalysisDate)break;
int slotIndex=(int)(((double)Cycle)%((double)(HoldingPeriod)));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TRADE DATE {0} , ANALYSIS DATE {1}",Utility.DateTimeToStringMMHDDHYYYY(TradeDate),Utility.DateTimeToStringMMHDDHYYYY(AnalysisDate)));
if(!ActivePositions.ContainsKey(slotIndex))
{
Positions positions=BuyPositions(TradeDate,AnalysisDate,CashBalance/((double)HoldingPeriod-(double)ActivePositions.Count),ActivePositions.SymbolsHeld());
MDTrace.WriteLine(LogLevel.DEBUG,"******************** B U Y ********************");
positions.Display();
if(CashBalance-positions.Exposure<0.00)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("********** Insufficient funds to make additional purchases.**************"));
break;
}
ActivePositions.Add(slotIndex,positions);
CashBalance-=positions.Exposure;
DisplayBalance();
}
else
{
Positions slotPositions=ActivePositions[slotIndex];
SellPositions(slotPositions,TradeDate);
MDTrace.WriteLine(LogLevel.DEBUG,"********************* S E L L *********************");
slotPositions.Display();
AllPositions.Add(slotPositions);
CashBalance+=slotPositions.MarketValue;
ActivePositions[slotIndex].Clear();
DisplayBalance();
double cashAllocation=CashBalance;
cashAllocation = Math.Min(CashBalance, (ActivePositions.GetExposure() + CashBalance) / (double)HoldingPeriod);
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("CASH ALLOCATION:{0}",Utility.FormatCurrency(cashAllocation)));
Positions positions = BuyPositions(TradeDate, AnalysisDate, cashAllocation, ActivePositions.SymbolsHeld());
MDTrace.WriteLine(LogLevel.DEBUG,"********************** B U Y ********************");
positions.Display();
if(CashBalance-positions.Exposure<=0.00)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("********** Insufficient funds to make additional purchases.**************"));
break;
}
ActivePositions[slotIndex]=positions;
CashBalance-=positions.Exposure;
DisplayBalance();
}
Cycle++;
TradeDate=dateGenerator.GetNextMonthEnd(TradeDate);
if(TradeDate>AnalysisDate)break;
} // WHILE TRUE
MDTrace.WriteLine(LogLevel.DEBUG,"RUN COMPLETE.");
if(null!=PathSessionFileName)SaveSession();
for(int slotIndex=0;slotIndex<HoldingPeriod;slotIndex++)
{
if(!ActivePositions.ContainsKey(slotIndex)||0==ActivePositions[slotIndex].Count())continue;
Positions slotPositions=ActivePositions[slotIndex];
SellPositions(slotPositions,AnalysisDate);
MDTrace.WriteLine(LogLevel.DEBUG,"********************* S E L L ********************");
slotPositions.Display();
AllPositions.Add(slotPositions);
CashBalance+=slotPositions.MarketValue;
ActivePositions[slotIndex].Clear();
}
MDTrace.WriteLine(LogLevel.DEBUG,"************** A L L P O S I T I O N S *************");
AllPositions=new Positions((from Position position in AllPositions orderby position.PurchaseDate ascending select position).ToList());
AllPositions.Display();
DisplayBalance();
backTestResult.Success=true;
backTestResult.CashBalance=CashBalance;
MGPriceCache.GetInstance().Dispose();
return backTestResult;
}
// **********************************************************************************************************************************************************
// **************************************************************** G E T E X P O S U R E / M A R K E T V A L U E*****************************************
// **********************************************************************************************************************************************************
public RealtimeGainLoss GetRealtimeGainLoss(DateTime tradeDate)
{
int count=ActivePositions.Count;
double marketValue=0.00;
double exposure=0.00;
RealtimeGainLoss gainLoss=new RealtimeGainLoss();
for(int slotIndex=0;slotIndex<count;slotIndex++)
{
List<Position> positions=ActivePositions[slotIndex];
if(null==positions||0==positions.Count)continue;
foreach(Position position in positions)
{
Price price=MGPriceCache.GetInstance().GetRealtimePrice(position.Symbol);
if(null==price){MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Cannot price {0} on {1}",position.Symbol,Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));continue;}
position.CurrentPrice=price.Close;
}
}
for(int slotIndex=0;slotIndex<count;slotIndex++)
{
List<Position> positions=ActivePositions[slotIndex];
if(null==positions||0==positions.Count)continue;
exposure+=(from Position position in positions select position.Exposure).Sum();
marketValue+=(from Position position in positions select position.MarketValue).Sum();
}
gainLoss.Exposure=exposure;
gainLoss.MarketValue=marketValue;
return gainLoss;
}
// **************************************************************************************************************************************************
// **************************************************************** S E L L P O S I T I O N S *****************************************************
// ***************************************************************************************************************************************************
private void SellPositions(Positions positions,DateTime sellDate)
{
foreach(Position position in positions)
{
Price price=MGPriceCache.GetInstance().GetPrice(position.Symbol,sellDate);
position.SellDate=sellDate;
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("**********Cannot locate a price for {0} on {1}**********",position.Symbol,Utility.DateTimeToStringMMHDDHYYYY(sellDate)));
position.CurrentPrice=position.PurchasePrice;
}
else position.CurrentPrice=price.Close;
}
}
private void SellPosition(Position position,DateTime sellDate)
{
Price price=MGPriceCache.GetInstance().GetPrice(position.Symbol,sellDate);
position.SellDate=sellDate;
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("**********Cannot locate a price for {0} on {1}**********",position.Symbol,Utility.DateTimeToStringMMHDDHYYYY(sellDate)));
position.CurrentPrice=position.PurchasePrice;
}
else position.CurrentPrice=price.Close;
}
// ***************************************************************************************************************************************************
// **************************************************************** B U Y P O S I T I O N S *****************************************************
// ***************************************************************************************************************************************************
//private Positions BuyPositions(DateTime tradeDate, DateTime analysisDate, double cash, List<String> symbolsHeld)
//{
// Positions positions = new Positions();
// int positionCount = 0;
// if (Configuration.BenchmarkMode) return BuyBenchmarkPositions(tradeDate, cash);
// MomentumCandidates momentumCandidates = MomentumGenerator.GenerateMomentum(tradeDate, symbolsHeld, Configuration);
// for (int index = 0; index < momentumCandidates.Count; index++)
// {
// MomentumCandidate momentumCandidate = momentumCandidates[index];
// Price price = MGPriceCache.GetInstance().GetPrice(momentumCandidate.Symbol, tradeDate);
// if (null == price)
// {
// MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Cannot locate a price for {0} on {1}", momentumCandidate.Symbol, Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));
// continue;
// }
// Position position = new Position();
// position.Symbol = momentumCandidate.Symbol;
// position.CumReturn252 = momentumCandidate.CumReturn252;
// position.IDIndicator = momentumCandidate.IDIndicator;
// position.MaxDrawdown = momentumCandidate.MaxDrawdown;
// position.MaxUpside = momentumCandidate.MaxUpside;
// position.PE = momentumCandidate.PE;
// position.Beta = momentumCandidate.Beta;
// position.ZacksRank = momentumCandidate.ZacksRank;
// position.Velocity = momentumCandidate.Velocity;
// position.Volume = momentumCandidate.Volume;
// position.Return1D = position.Return1D;
// position.PurchaseDate = tradeDate;
// position.PurchasePrice = price.Close;
// position.CurrentPrice = price.Close;
// position.Shares = (int)Math.Floor((cash / (double)MaxPositions) / position.PurchasePrice);
// if (0 == position.Shares) continue; // if not able to purchase any shares, for example, if the share price exceeds our purchasing power then move on to the next candidate
// WatchListDA.AddToWatchList(position.Symbol, "Momentum");
// positions.Add(position);
// positionCount++;
// if (positionCount >= MaxPositions) break;
// }
// if (0 == positions.Count && Configuration.UseFallbackCandidate) // if we don't get any signals then consider the fallback candidate options as per the configuration file
// {
// String fallbackCandidate = Configuration.FallbackCandidate;
// if (null != Configuration.FallbackCandidateBestOf && !"".Equals(Configuration.FallbackCandidateBestOf))
// {
// fallbackCandidate = CandidateSelector.SelectBestCandidateSymbol(Utility.ToList(Configuration.FallbackCandidateBestOf), Configuration.FallbackCandidate, tradeDate);
// }
// Price price = MGPriceCache.GetInstance().GetPrice(fallbackCandidate, tradeDate);
// if (null == price)
// {
// MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Cannot locate a price for {0} on {1}", fallbackCandidate, Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));
// return positions;
// }
// Position position = new Position();
// position.Symbol = fallbackCandidate;
// position.PurchaseDate = tradeDate;
// position.PurchasePrice = price.Close;
// position.CurrentPrice = price.Close;
// position.Shares = (int)Math.Floor(cash / position.PurchasePrice);
// positions.Add(position);
// }
// return positions;
//}
private Positions BuyPositions(DateTime tradeDate, DateTime analysisDate, double cash, List<String> symbolsHeld)
{
// return BuyPositionsWithSharpeRatioRiskAllocation(tradeDate, analysisDate, cash, symbolsHeld);
return BuyPositionsWithBetaRiskAllocation(tradeDate, analysisDate, cash, symbolsHeld);
}
// ******************************************************************************************************************************************************************
// ********************************************** U S E S H A R P E R A T I O F O R P O S I T I O N S I Z I N G ***********************************************
// ******************************************************************************************************************************************************************
public class SRRiskAllocation
{
public String Symbol { get; set; }
public double SharpeRatioBaseOne { get; set; } // This applies an offset to the Sharpe Ratios so that we are working with zero based SharpeRatios for allocation purposes.
public double Weight { get; set; }
public double Allocation { get; set; }
public int Shares { get; set; }
}
private Positions BuyPositionsWithSharpeRatioRiskAllocation(DateTime tradeDate, DateTime analysisDate, double cash, List<String> symbolsHeld)
{
Positions positions = new Positions();
if (Configuration.BenchmarkMode) return BuyBenchmarkPositions(tradeDate, cash);
MomentumCandidates momentumCandidates = MomentumGenerator.GenerateMomentum(tradeDate, symbolsHeld, Configuration);
positions = PositonSizingWithSharpeRatioRiskAllocation(momentumCandidates, tradeDate, cash);
if (0 == positions.Count && Configuration.UseFallbackCandidate) // if we don't get any signals then consider the fallback candidate options as per the configuration file
{
String fallbackCandidate = Configuration.FallbackCandidate;
if (null != Configuration.FallbackCandidateBestOf && !"".Equals(Configuration.FallbackCandidateBestOf))
{
fallbackCandidate = CandidateSelector.SelectBestCandidateSymbol(Utility.ToList(Configuration.FallbackCandidateBestOf), Configuration.FallbackCandidate, tradeDate);
}
Price price = MGPriceCache.GetInstance().GetPrice(fallbackCandidate, tradeDate);
if (null == price)
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Cannot locate a price for {0} on {1}", fallbackCandidate, Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));
return positions;
}
Position position = new Position();
position.Symbol = fallbackCandidate;
position.PurchaseDate = tradeDate;
position.PurchasePrice = price.Close;
position.CurrentPrice = price.Close;
position.Shares = (int)Math.Floor(cash / position.PurchasePrice);
positions.Add(position);
}
return positions;
}
private Positions PositonSizingWithSharpeRatioRiskAllocation(MomentumCandidates momentumCandidates,DateTime tradeDate,double cash)
{
Positions positions = new Positions();
List<SRRiskAllocation> riskAllocations = new List<SRRiskAllocation>();
for (int index = 0; index < momentumCandidates.Count; index++)
{
MomentumCandidate momentumCandidate = momentumCandidates[index];
Price price = MGPriceCache.GetInstance().GetPrice(momentumCandidate.Symbol, tradeDate);
if (null == price)
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Cannot locate a price for {0} on {1}", momentumCandidate.Symbol, Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));
continue;
}
Position position = new Position();
position.Symbol = momentumCandidate.Symbol;
position.CumReturn252 = momentumCandidate.CumReturn252;
position.IDIndicator = momentumCandidate.IDIndicator;
position.MaxDrawdown = momentumCandidate.MaxDrawdown;
position.MaxUpside = momentumCandidate.MaxUpside;
position.PE = momentumCandidate.PE;
position.Beta = momentumCandidate.Beta;
position.ZacksRank = momentumCandidate.ZacksRank;
position.Velocity = momentumCandidate.Velocity;
position.Volume = momentumCandidate.Volume;
position.Return1D = position.Return1D;
position.PurchaseDate = tradeDate;
position.PurchasePrice = price.Close;
position.CurrentPrice = price.Close;
position.SharpeRatio = SharpeRatioGenerator.GenerateSharpeRatio(position.Symbol, tradeDate);
position.Shares = 0.00;
if (double.IsNaN(position.SharpeRatio)||0.00==position.SharpeRatio) continue;
WatchListDA.AddToWatchList(position.Symbol, "Momentum");
positions.Add(position);
}
if (null == positions || 0 == positions.Count) return positions;
positions = new Positions(positions.Take(MaxPositions).ToList());
double minSharpeRatio = positions.Min(x => x.SharpeRatio);
for (int index = 0; index < positions.Count; index++)
{
Position position = positions[index];
SRRiskAllocation riskAllocation = new SRRiskAllocation();
riskAllocation.Symbol = position.Symbol;
riskAllocation.SharpeRatioBaseOne = minSharpeRatio < 0.00 ? position.SharpeRatio + Math.Abs(minSharpeRatio) + 1.00 : position.SharpeRatio;
riskAllocations.Insert(index, riskAllocation);
}
double sumSharpeRatioBaseOne = riskAllocations.Sum(x => x.SharpeRatioBaseOne);
riskAllocations.Select(t => t.Weight = t.SharpeRatioBaseOne/sumSharpeRatioBaseOne).ToList();
riskAllocations.Select(t => t.Allocation = t.Weight*cash).ToList();
for (int index = 0; index < positions.Count; index++)
{
Position position = positions[index];
SRRiskAllocation riskAllocation = riskAllocations[index];
if (!position.Symbol.Equals(riskAllocation.Symbol)) throw new Exception("RiskAllocation items are not in sequence with positions.");
position.Shares = Math.Floor(riskAllocation.Allocation/position.PurchasePrice);
}
positions =new Positions(positions.Where(x => x.Shares > 0).ToList());
return positions;
}
// *********************************************************************************************************************************************************************
// ********************************************************* E N D S H A R P E R A T I O R I S K A L L O C A T I O N *****************************************
// *********************************************************************************************************************************************************************
// ******************************************************************************************************************************************************************
// ********************************************** U S E B E T A F O R P O S I T I O N S I Z I N G ***********************************************
// ******************************************************************************************************************************************************************
public class BetaRiskAllocation
{
public String Symbol { get; set; }
public double TargetBetaOverBeta { get; set; }
public double Weight { get; set; }
public double Allocation { get; set; }
public int Shares { get; set; }
}
private Positions BuyPositionsWithBetaRiskAllocation(DateTime tradeDate, DateTime analysisDate, double cash, List<String> symbolsHeld)
{
Positions positions = new Positions();
if (Configuration.BenchmarkMode) return BuyBenchmarkPositions(tradeDate, cash);
MomentumCandidates momentumCandidates = MomentumGenerator.GenerateMomentum(tradeDate, symbolsHeld, Configuration);
positions = PositonSizingWithBetaRiskAllocation(momentumCandidates, tradeDate, cash);
if (0 == positions.Count && Configuration.UseFallbackCandidate) // if we don't get any signals then consider the fallback candidate options as per the configuration file
{
String fallbackCandidate = Configuration.FallbackCandidate;
if (null != Configuration.FallbackCandidateBestOf && !"".Equals(Configuration.FallbackCandidateBestOf))
{
fallbackCandidate = CandidateSelector.SelectBestCandidateSymbol(Utility.ToList(Configuration.FallbackCandidateBestOf), Configuration.FallbackCandidate, tradeDate);
}
Price price = MGPriceCache.GetInstance().GetPrice(fallbackCandidate, tradeDate);
if (null == price)
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Cannot locate a price for {0} on {1}", fallbackCandidate, Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));
return positions;
}
Position position = new Position();
position.Symbol = fallbackCandidate;
position.PurchaseDate = tradeDate;
position.PurchasePrice = price.Close;
position.CurrentPrice = price.Close;
position.Shares = (int)Math.Floor(cash / position.PurchasePrice);
positions.Add(position);
}
return positions;
}
private Positions PositonSizingWithBetaRiskAllocation(MomentumCandidates momentumCandidates, DateTime tradeDate, double cash)
{
Positions positions = new Positions();
List<BetaRiskAllocation> riskAllocations = new List<BetaRiskAllocation>();
for (int index = 0; index < momentumCandidates.Count; index++)
{
MomentumCandidate momentumCandidate = momentumCandidates[index];
Price price = MGPriceCache.GetInstance().GetPrice(momentumCandidate.Symbol, tradeDate);
if (null == price)
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Cannot locate a price for {0} on {1}", momentumCandidate.Symbol, Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));
continue;
}
Position position = new Position();
position.Symbol = momentumCandidate.Symbol;
position.CumReturn252 = momentumCandidate.CumReturn252;
position.IDIndicator = momentumCandidate.IDIndicator;
position.MaxDrawdown = momentumCandidate.MaxDrawdown;
position.MaxUpside = momentumCandidate.MaxUpside;
position.PE = momentumCandidate.PE;
position.Beta = BetaGenerator.Beta(position.Symbol, tradeDate, 6); // Use the beta generator to get a 6 month beta.
if (double.IsNaN(position.Beta) || position.Beta <= 0.00) continue;
position.ZacksRank = momentumCandidate.ZacksRank;
position.Velocity = momentumCandidate.Velocity;
position.Volume = momentumCandidate.Volume;
position.Return1D = position.Return1D;
position.PurchaseDate = tradeDate;
position.PurchasePrice = price.Close;
position.CurrentPrice = price.Close;
position.SharpeRatio = SharpeRatioGenerator.GenerateSharpeRatio(position.Symbol, tradeDate); // This is just for reference.
position.Shares = 0.00;
WatchListDA.AddToWatchList(position.Symbol, "Momentum");
positions.Add(position);
}
if (null == positions || 0 == positions.Count) return positions;
positions = new Positions(positions.Take(MaxPositions).ToList());
double targetBeta = 1.00;
double positionCount = positions.Count;
for (int index = 0; index < positions.Count; index++)
{
Position position = positions[index];
BetaRiskAllocation riskAllocation = new BetaRiskAllocation();
riskAllocation.Symbol = position.Symbol;
riskAllocation.TargetBetaOverBeta = targetBeta / position.Beta;
riskAllocations.Insert(index, riskAllocation);
}
double sumTargetBetaOverBeta = riskAllocations.Sum(x => x.TargetBetaOverBeta);
riskAllocations.Select(t => t.Weight = t.TargetBetaOverBeta / sumTargetBetaOverBeta).ToList();
riskAllocations.Select(t => t.Allocation = t.Weight *cash).ToList();
for (int index = 0; index < positions.Count; index++)
{
Position position = positions[index];
BetaRiskAllocation riskAllocation = riskAllocations[index];
if (!position.Symbol.Equals(riskAllocation.Symbol)) throw new Exception("RiskAllocation items are not in sequence with positions.");
position.Shares = Math.Floor(riskAllocation.Allocation / position.PurchasePrice);
}
positions = new Positions(positions.Where(x => x.Shares > 0).ToList());
return positions;
}
// *********************************************************************************************************************************************************************
// ************************************************************* E N D B E T A R I S K A L L O C A T I O N *****************************************************
// *********************************************************************************************************************************************************************
// ***************************************************************************************************************************************************************************
private Positions BuyBenchmarkPositions(DateTime tradeDate, double cash)
{
Positions positions = new Positions();
Price benchmarkPrice = MGPriceCache.GetInstance().GetPrice(Configuration.BenchmarkModeSymbol, tradeDate);
if (null == benchmarkPrice)
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Cannot locate a price for {0} on {1}", Configuration.BenchmarkModeSymbol, Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));
return positions;
}
Position position = new Position();
position.Symbol = Configuration.BenchmarkModeSymbol;
position.PurchaseDate = tradeDate;
position.PurchasePrice = benchmarkPrice.Close;
position.Shares = (int)Math.Floor(cash / position.PurchasePrice);
if (0 == position.Shares) return positions;
WatchListDA.AddToWatchList(position.Symbol, "Momentum");
positions.Add(position);
return positions;
}
// ***********************************************************************************************************************************************************
private void DisplayBalance()
{
MDTrace.WriteLine(LogLevel.DEBUG,"*******************************************");
MDTrace.WriteLine(LogLevel.DEBUG,"EXPOSURE,AVAILABLE CASH,TOTAL ACCOUNT");
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0},{1},{2}",Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetExposure())),Utility.AddQuotes(Utility.FormatCurrency(CashBalance)),Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetMarketValue()+CashBalance))));
for (int slotIndex = 0; slotIndex < ActivePositions.Count; slotIndex++)
{
Positions slotPositions = ActivePositions[slotIndex];
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("SLOT:{0} EXPOSURE:{1}",slotIndex,Utility.FormatCurrency(slotPositions.Exposure)));
}
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("TOTAL EXPOSURE: {0}",Utility.FormatCurrency(ActivePositions.GetExposure())));
MDTrace.WriteLine(LogLevel.DEBUG,"*******************************************");
}
private void DisplayBalanceFromPositions()
{
MDTrace.WriteLine(LogLevel.DEBUG,"EXPOSURE,GAIN/LOSS,GAIN/LOSS(%),AVAILABLE CASH,TOTAL ACCOUNT");
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0},{1},{2},{3},{4}",
Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetExposure())),
Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetGainLoss())),
Utility.AddQuotes(Utility.FormatPercent(ActivePositions.GetGainLossPercent())),
Utility.AddQuotes(Utility.FormatCurrency(CashBalance)),
Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetMarketValue()+CashBalance))));
}
private void DisplayBalance(RealtimeGainLoss gainLoss)
{
MDTrace.WriteLine(LogLevel.DEBUG,"EXPOSURE,GAIN/LOSS,GAIN/LOSS(%),AVAILABLE CASH,TOTAL ACCOUNT");
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0},{1},{2},{3},{4}",
Utility.AddQuotes(Utility.FormatCurrency(ActivePositions.GetExposure())),
Utility.AddQuotes(Utility.FormatCurrency(gainLoss.GainLoss)),
Utility.AddQuotes(Utility.FormatPercent(gainLoss.GainLossPercent)),
Utility.AddQuotes(Utility.FormatCurrency(CashBalance)),
Utility.AddQuotes(Utility.FormatCurrency(gainLoss.MarketValue+CashBalance))));
}
// ****************************************************************************************************************************************
// ************************************************************* C O N T R O L T O D A Y ***********************************************
// ****************************************************************************************************************************************
public DateTime Today()
{
return DateTime.Now;
}
// ****************************************************************************************************************************************
// **************************************************************** S E S S I O N M A N A G E M E N T ***********************************
// ****************************************************************************************************************************************
public MGSessionParams RestoreSession()
{
try
{
MGSessionManager sessionManager=new MGSessionManager();
if(!MGSessionManager.SessionAvailable(PathSessionFileName))return null;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Restoring session from '{0}'",PathSessionFileName));
MGSessionParams sessionParams=MGSessionManager.RestoreSession(PathSessionFileName);
TradeDate=sessionParams.TradeDate;
if(TradeDate.Date<AnalysisDate.Date)TradeDate=AnalysisDate;
StartDate=sessionParams.StartDate;
Configuration=sessionParams.Configuration;
ActivePositions=sessionParams.ActivePositions;
AllPositions=sessionParams.AllPositions;
Cycle=sessionParams.Cycle;
CashBalance=sessionParams.CashBalance;
return sessionParams;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
return null;
}
}
public void SaveSession()
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Saving session to '{0}'",PathSessionFileName));
MGSessionParams sessionParams=new MGSessionParams();
MGSessionManager sessionManager=new MGSessionManager();
sessionParams.LastUpdated=Today();
sessionParams.TradeDate=TradeDate;
sessionParams.StartDate=StartDate;
sessionParams.AnalysisDate=AnalysisDate;
sessionParams.Configuration=Configuration;
sessionParams.ActivePositions=ActivePositions;
sessionParams.AllPositions=AllPositions;
sessionParams.Cycle=Cycle;
sessionParams.CashBalance=CashBalance;
sessionManager.SaveSession(sessionParams,PathSessionFileName);
}
}
}