diff --git a/MarketDataLib/Generator/CMTrend/CMTPositions.cs b/MarketDataLib/Generator/CMTrend/CMTPositions.cs index 21f85e0..6b27cdf 100644 --- a/MarketDataLib/Generator/CMTrend/CMTPositions.cs +++ b/MarketDataLib/Generator/CMTrend/CMTPositions.cs @@ -1,17 +1,13 @@ 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.Reflection; +using MarketData.Generator.Interface; namespace MarketData.Generator.CMTrend { - public class Position + public class Position : IPosition { public Position() { diff --git a/MarketDataLib/Generator/CMTrend/CMTTrendModel..cs b/MarketDataLib/Generator/CMTrend/CMTTrendModel..cs index 8587d04..0b5b415 100644 --- a/MarketDataLib/Generator/CMTrend/CMTTrendModel..cs +++ b/MarketDataLib/Generator/CMTrend/CMTTrendModel..cs @@ -8,10 +8,7 @@ using MarketData.Utils; using System.Linq; using MarketData.Helper; using MarketData.Numerical; -using System.Threading; -using MarketData.Integration; using MarketData.Cache; -using System.IO; using MarketData.Generator.Indicators; using MarketData.Generator.Model; using StopLimit=MarketData.Generator.Model.StopLimit; diff --git a/MarketDataLib/Generator/MGSHMomentum/HedgeManager.cs b/MarketDataLib/Generator/MGSHMomentum/HedgeManager.cs index fd66573..e58c56b 100644 --- a/MarketDataLib/Generator/MGSHMomentum/HedgeManager.cs +++ b/MarketDataLib/Generator/MGSHMomentum/HedgeManager.cs @@ -283,6 +283,7 @@ namespace MarketData.Generator.MGSHMomentum MDTrace.WriteLine(LogLevel.DEBUG,String.Format("*****************************************************************************************")); StopLimit newStopLimit=new StopLimit { + StopLimitId=position.Symbol + Utility.DateTimeToStringYYYYMMDDMMSSTT(position.PurchaseDate), Symbol=position.Symbol, AnalysisDate=analysisDate, PreviousStop=0.00==position.TrailingStopLimit?position.InitialStopLimit:position.TrailingStopLimit, diff --git a/MarketDataLib/Generator/MGSHMomentum/MGSHBacktest.cs b/MarketDataLib/Generator/MGSHMomentum/MGSHBacktest.cs index 24b845c..655eb2f 100644 --- a/MarketDataLib/Generator/MGSHMomentum/MGSHBacktest.cs +++ b/MarketDataLib/Generator/MGSHMomentum/MGSHBacktest.cs @@ -22,12 +22,13 @@ namespace MarketData.Generator.MGSHMomentum private MGSHConfiguration Configuration{get;set;} private int HoldingPeriod{get{return Configuration.HoldingPeriod;}} private int MaxPositions{get{return Configuration.MaxPositions;}} - private List NoTradeSymbols{get{return Utility.ToList(Configuration.NoTradeSymbols);}} + private int MaxPricingExceptions{get{return Configuration.MaxPricingExceptions;}} + //private List NoTradeSymbols{get{return Utility.ToList(Configuration.NoTradeSymbols);}} private MGSHActivePositions ActivePositions{get;set;} -// private SMSNotifications SMSNotifications{get;set;} private StopLimits StopLimits{get;set;} private MGSHPositions AllPositions{get;set;} private MGSHPositions HedgePositions{get;set;} + private MGSHPricingExceptions PricingExceptions{get;set;} private int Cycle{get;set;} private DateTime TradeDate{get;set;} private DateTime StartDate{get;set;} @@ -118,6 +119,61 @@ namespace MarketData.Generator.MGSHMomentum } } + // ****************************************************************************************************************************************************** + // *************************************************************************** E D I T ****************************************************************** + // ****************************************************************************************************************************************************** + public bool EditPosition(String symbol,DateTime purchaseDate,double purchasePrice,double initialStop,double trailingStop,String sessionFile) + { + if (!MGSHSessionManager.IsValidSessionFile(sessionFile)) + { + MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Invalid session file '{0}'.", sessionFile)); + return false; + } + PathSessionFileName = sessionFile; + MGSHSessionParams sessionParams=MGSHSessionManager.RestoreSession(PathSessionFileName); + Configuration = sessionParams.Configuration; + TradeDate = sessionParams.TradeDate; + StartDate = sessionParams.StartDate; + AnalysisDate = sessionParams.AnalysisDate; + Cycle=sessionParams.Cycle; + sessionParams.LastUpdated = DateTime.Now; + //sessionParams.CMTParams.AnalysisDate=DateTime.Now; + StopLimits=sessionParams.StopLimits; + ActivePositions=sessionParams.ActivePositions; + AllPositions=sessionParams.AllPositions; + HedgePositions=sessionParams.HedgePositions; + CashBalance=sessionParams.CashBalance; + NonTradeableCash=sessionParams.NonTradeableCash; + HedgeCashBalance=sessionParams.HedgeCashBalance; + PricingExceptions=sessionParams.PricingExceptions; + if(!BackupSession())return false; + MGSHPositions activePositions = ActivePositions.GetPositions(); + MGSHPosition position = activePositions.Where(x => x.Symbol.Equals(symbol) && x.PurchaseDate.Equals(purchaseDate)).FirstOrDefault(); + if (null == position) + { + MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Cannot locate position for symbol '{0}' purchased on {1}.", symbol, purchaseDate.ToShortDateString())); + return false; + } + if (!position.PurchaseDate.Equals(purchaseDate)) position.PurchaseDate = purchaseDate; + if (!position.TrailingStopLimit.Equals(trailingStop)) position.TrailingStopLimit = trailingStop; + if (!position.InitialStopLimit.Equals(initialStop)) position.InitialStopLimit = initialStop; + if (!position.PurchasePrice.Equals(purchasePrice)) + { + MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Adjusting Cash for Position for symbol '{0}' purchased on {1}. Original Price: {2} New Price: {3} Change in Cash: {4}", symbol, purchaseDate.ToShortDateString(), Utility.FormatCurrency(position.PurchasePrice), Utility.FormatCurrency(purchasePrice), Utility.FormatCurrency((position.PurchasePrice - purchasePrice) * position.Shares))); + MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Adjusting R for Position for symbol '{0}' purchased on {1}. Original R: {2} New R: {3} ", symbol, purchaseDate.ToShortDateString(), Utility.FormatNumber(position.R, 2), Utility.FormatNumber(position.PositionRiskPercentDecimal * purchasePrice))); + CashBalance += (position.PurchasePrice - purchasePrice) * position.Shares; + position.Comment = (null == position.Comment ? "" : position.Comment + ".") + String.Format("Price changed on {0} from {1} to {2}", DateTime.Now.ToShortDateString(), Utility.FormatCurrency(position.PurchasePrice), Utility.FormatCurrency(purchasePrice)); + position.PurchasePrice = purchasePrice; + } + SaveSession(); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Position for symbol '{0}' purchased on {1} has been modified and saved.",symbol,purchaseDate.ToShortDateString())); + return true; + } + + // ****************************************************************************************************************************************************** + // **************************************************************** S T A T I S T I C S ****************************************************************** + // ****************************************************************************************************************************************************** + // Calcualtes the expectation for the model ( Percent of Winning Trades * Average Gain)/(Percent Losing Trades * Average Loss) // The expectation should be above zero // Using the AllPositions collection ignored open positions and active hedge positions @@ -308,6 +364,7 @@ namespace MarketData.Generator.MGSHMomentum ActivePositions=new MGSHActivePositions(); AllPositions=new MGSHPositions(); HedgePositions=new MGSHPositions(); + PricingExceptions=new MGSHPricingExceptions(); StartDate=paramStartDate; TradeDate=paramStartDate; AnalysisDate=paramAnalysisDate; @@ -450,14 +507,16 @@ namespace MarketData.Generator.MGSHMomentum if(CashBalance-positions.Exposure<=0.00) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("********** Insufficient funds to make additional purchases.**************")); - return; } - MDTrace.WriteLine(LogLevel.DEBUG,"********************** B U Y ********************"); - if(!ActivePositions.ContainsKey(slotIndex))ActivePositions.Add(slotIndex, positions); - else ActivePositions[slotIndex].AddRange(positions); - CashBalance-=positions.Exposure; - SetInitialStopLimitsForNewPositions(TradeDate, positions); - ActivePositions[slotIndex].Display(); + else + { + MDTrace.WriteLine(LogLevel.DEBUG,"********************** B U Y ********************"); + if(!ActivePositions.ContainsKey(slotIndex))ActivePositions.Add(slotIndex, positions); + else ActivePositions[slotIndex].AddRange(positions); + CashBalance-=positions.Exposure; + SetInitialStopLimitsForNewPositions(TradeDate, positions); + ActivePositions[slotIndex].Display(); + } UpdateStopLimitsForActivePositions(TradeDate); DisplayBalance(); } @@ -582,7 +641,8 @@ namespace MarketData.Generator.MGSHMomentum shortPosition.PurchasePrice = shortPositionPrice.Close; shortPosition.CurrentPrice = shortPositionPrice.Close; shortPosition.InitialStopLimit = shortPosition.PurchasePrice - ( shortPosition.PurchasePrice * Configuration.HedgeRiskPercentDecimal); - shortPosition.R = shortPosition.PurchasePrice - shortPosition.InitialStopLimit; + shortPosition.PositionRiskPercentDecimal = Configuration.HedgeRiskPercentDecimal; // The risk percent + shortPosition.R = shortPosition.PositionRiskPercentDecimal * shortPosition.PurchasePrice; // PositionRiskPercentDecimal*PurchasePrice shortPosition.TrailingStopLimit = shortPosition.InitialStopLimit; shortPosition.LastStopAdjustment = Utility.Epoch; shortPosition.Shares = (int)Math.Floor(cashAllocation / shortPositionPrice.Close); @@ -685,7 +745,8 @@ namespace MarketData.Generator.MGSHMomentum foreach(MGSHPosition position in positions) { position.InitialStopLimit = position.PurchasePrice - (position.PurchasePrice * Configuration.StopLimitRiskPercentDecimal); - position.R = position.PurchasePrice - position.InitialStopLimit; + position.PositionRiskPercentDecimal = Configuration.StopLimitRiskPercentDecimal; // = position.PurchasePrice - position.InitialStopLimit; + position.R = position.PositionRiskPercentDecimal * position.PurchasePrice; // PositionRiskPercentDecimal*PurchasePrice position.TrailingStopLimit = position.InitialStopLimit; position.LastStopAdjustment = Utility.Epoch; MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Initial stop limit for {0} on {1} with PurchsePrice:{2} is {3}, Shares:{4} Risk:{5}%", @@ -718,10 +779,23 @@ namespace MarketData.Generator.MGSHMomentum foreach(MGSHPosition position in positions) { Price price=GBPriceCache.GetInstance().GetPrice(position.Symbol,analysisDate); +// Incorporate Pricing Exceptions if(null==price) { - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[UpdateStopLimitsForActivePositions] No price for {0} on {1}. Cannot evaluate stop limit.",position.Symbol,analysisDate.ToShortDateString())); + int exceptionCount=AddPricingException(position.Symbol); + if(exceptionCount>MaxPricingExceptions) + { + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[UpdateStopLimitsForActivePositions] Selling {0} on {1} because price exceptions exceeds maximum of {2}",position.Symbol,analysisDate.ToShortDateString(),MaxPricingExceptions)); + price=GBPriceCache.GetInstance().GetPriceOrLatestAvailable(position.Symbol,analysisDate); + position.SellDate=analysisDate; + position.CurrentPrice=price.Close; + position.Comment="Close due to pricing exceptions."; + CashBalance+=position.MarketValue; + AllPositions.Add(position); + closedPositions.Add(position); + } + else MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[UpdateStopLimitsForActivePositions] Cannot determine price for {0} on {1}",position.Symbol,analysisDate.ToShortDateString())); continue; } @@ -733,6 +807,7 @@ namespace MarketData.Generator.MGSHMomentum } position.CurrentPrice = price.Close; + RemovePricingException(position.Symbol); if(price.Low < position.TrailingStopLimit && !position.PurchaseDate.Equals(analysisDate)) { position.SellDate = analysisDate; @@ -741,10 +816,10 @@ namespace MarketData.Generator.MGSHMomentum CashBalance += position.MarketValue; AllPositions.Add(position); closedPositions.Add(position); - } - else if(price.Low > position.PurchasePrice) // If the Low price is above our purchase price then re-evaluate the stop + } + else if (price.Low > position.PurchasePrice) // If the Low price is above our purchase price then re-evaluate the stop { - EvaluateStopPrice(analysisDate,price,position); + EvaluateStopPrice(analysisDate, price, position); } } } @@ -777,9 +852,9 @@ namespace MarketData.Generator.MGSHMomentum // only adjust stops if we are trending up Prices prices=GBPriceCache.GetInstance().GetPrices(position.Symbol,analysisDate,Configuration.StopLimitPriceTrendDays); PriceTrendIndicatorResult priceTrendIndicatorResult=PriceTrendIndicator.IsUptrend(prices,Configuration.StopLimitPriceTrendDays); - if(!priceTrendIndicatorResult.IsUpTrend) + if (!priceTrendIndicatorResult.IsUpTrend) { - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("{0} does not have upward price trend (higher highs and higher lows for {1} consecutive days), will not adjust stop price",position.Symbol,Configuration.StopLimitPriceTrendDays)); + MDTrace.WriteLine(LogLevel.DEBUG, String.Format("{0} does not have upward price trend (higher highs and higher lows for {1} consecutive days), will not adjust stop price", position.Symbol, Configuration.StopLimitPriceTrendDays)); return changed; } @@ -793,7 +868,7 @@ namespace MarketData.Generator.MGSHMomentum { trailingStopScaled=GetStopLimitWithScalingAverageTrueRange(analysisDate,currentPrice,position,trailingStop); trailingStop=Math.Max(trailingStop,trailingStopScaled); - if(trailingStop>=currentPrice.Low||trailingStop>=currentPrice.Close) + if(trailingStop>=currentPrice.Low || trailingStop>=currentPrice.Close) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("The calculated trailing stop for {0} of {1} would be higher than the Low/Close of {2}.",position.Symbol,Utility.FormatCurrency(trailingStop),Utility.FormatCurrency(currentPrice.Low))); return changed; @@ -804,10 +879,18 @@ namespace MarketData.Generator.MGSHMomentum return changed; } MDTrace.WriteLine(LogLevel.DEBUG,String.Format("****************************** A D J U S T S T O P L I M I T ************************")); - MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Adjusting StopLimit after {0} days for {1} from {2} to {3}. Purchase Price: {4} Current Price:{5} Spread:{6} Shares:{7}",daysHeld,position.Symbol,Utility.FormatCurrency(position.TrailingStopLimit),Utility.FormatCurrency(trailingStop),Utility.FormatCurrency(position.PurchasePrice),Utility.FormatCurrency(currentPrice.Close),Utility.FormatPercent((currentPrice.Close-trailingStop)/trailingStop),Utility.FormatNumber(position.Shares,2))); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Adjusting StopLimit after {0} days for {1} from {2} to {3}. Purchase Price: {4} Current Price:{5} Spread:{6} Shares:{7}", + daysHeld, + position.Symbol,Utility.FormatCurrency(position.TrailingStopLimit), + Utility.FormatCurrency(trailingStop), + Utility.FormatCurrency(position.PurchasePrice), + Utility.FormatCurrency(currentPrice.Close), + Utility.FormatPercent((currentPrice.Close-trailingStop)/trailingStop), + Utility.FormatNumber(position.Shares,2))); MDTrace.WriteLine(LogLevel.DEBUG,String.Format("*****************************************************************************************")); StopLimit newStopLimit=new StopLimit { + StopLimitId=position.Symbol + Utility.DateTimeToStringYYYYMMDDMMSSTT(position.PurchaseDate), Symbol=position.Symbol, AnalysisDate=analysisDate, PreviousStop=0.00==position.TrailingStopLimit?position.InitialStopLimit:position.TrailingStopLimit, @@ -846,6 +929,7 @@ namespace MarketData.Generator.MGSHMomentum MDTrace.WriteLine(LogLevel.DEBUG,String.Format("****************************************************************************************")); StopLimit newStopLimit=new StopLimit { + StopLimitId=position.Symbol + Utility.DateTimeToStringYYYYMMDDMMSSTT(position.PurchaseDate), Symbol=position.Symbol, AnalysisDate=analysisDate, PreviousStop=0.00==position.TrailingStopLimit?position.InitialStopLimit:position.TrailingStopLimit, @@ -891,15 +975,46 @@ namespace MarketData.Generator.MGSHMomentum return stopLimit; } -// ********************************************************************************************************************************************************** -// ***************************************************** S T O P L I M I T C O L L E C T I O N M A I N T E N A N C E *********************************** -// ********************************************************************************************************************************************************** +// ************************************************************************************************************************************** +// ************************************************** S T O P L I M I T C O L L E C T I O N M A I N T E N A N C E ****************** +// ************************************************************************************************************************************** private void AddStopLimit(StopLimit stopLimit) { if(null == StopLimits)StopLimits = new StopLimits(); StopLimits.Add(stopLimit); } +// ************************************************************************************************************************************************ +// ********************************************* P R I C I N G E X C E P T I O N C O L L E C T I O N M A I N T E N A N C E ****************** +// ************************************************************************************************************************************************ + + private int AddPricingException(String symbol) + { + MGSHPricingException pricingException = PricingExceptions.Where(x => x.Symbol.Equals(symbol)).FirstOrDefault(); + if (null == pricingException) + { + pricingException = new MGSHPricingException(symbol, 1); + PricingExceptions.Add(pricingException); + PricingExceptions.Add(pricingException); + } + else pricingException.ExceptionCount++; + return pricingException.ExceptionCount; + } + + private void RemovePricingException(String symbol) + { + MGSHPricingException pricingException = PricingExceptions.Where(x => x.Symbol.Equals(symbol)).FirstOrDefault(); + if (null == pricingException) return; + PricingExceptions.Remove(pricingException); + } + + private bool HasPricingException(String symbol) + { + MGSHPricingException pricingException = PricingExceptions.Where(x => x.Symbol.Equals(symbol)).FirstOrDefault(); + if (null == pricingException) return false; + return true; + } + // ****************************************************************************************************************************************************** // ****************************************************************************************************************************************************** // ****************************************************************************************************************************************************** @@ -1185,6 +1300,7 @@ namespace MarketData.Generator.MGSHMomentum AllPositions=sessionParams.AllPositions; HedgePositions=sessionParams.HedgePositions; StopLimits=sessionParams.StopLimits; + PricingExceptions = sessionParams.PricingExceptions; Cycle=sessionParams.Cycle; CashBalance=sessionParams.CashBalance; NonTradeableCash=sessionParams.NonTradeableCash; @@ -1211,9 +1327,33 @@ namespace MarketData.Generator.MGSHMomentum sessionParams.HedgePositions=HedgePositions; sessionParams.Cycle=Cycle; sessionParams.StopLimits=StopLimits; + sessionParams.PricingExceptions=PricingExceptions; sessionParams.CashBalance=CashBalance; sessionParams.NonTradeableCash=NonTradeableCash; sessionManager.SaveSession(sessionParams,PathSessionFileName); } + + public bool BackupSession() + { + String[] parts=PathSessionFileName.Split('.'); + String backupFileName=parts[0]+"_"+Utility.DateTimeToStringYYYYMMDDMMSSTT(DateTime.Now)+".bak"; + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Saving session to '{0}'",backupFileName)); + MGSHSessionParams sessionParams=new MGSHSessionParams(); + MGSHSessionManager sessionManager=new MGSHSessionManager(); + sessionParams.LastUpdated=Today(); + sessionParams.TradeDate=TradeDate; + sessionParams.StartDate=StartDate; + sessionParams.AnalysisDate=AnalysisDate; + sessionParams.Configuration=Configuration; + sessionParams.ActivePositions=ActivePositions; + sessionParams.AllPositions=AllPositions; + sessionParams.HedgePositions=HedgePositions; + sessionParams.Cycle=Cycle; + sessionParams.StopLimits=StopLimits; + sessionParams.PricingExceptions=PricingExceptions; + sessionParams.CashBalance=CashBalance; + sessionParams.NonTradeableCash=NonTradeableCash; + return sessionManager.SaveSession(sessionParams,backupFileName); + } } } diff --git a/MarketDataLib/Generator/MGSHMomentum/MGSHConfiguration.cs b/MarketDataLib/Generator/MGSHMomentum/MGSHConfiguration.cs index d876f29..1d059ae 100644 --- a/MarketDataLib/Generator/MGSHMomentum/MGSHConfiguration.cs +++ b/MarketDataLib/Generator/MGSHMomentum/MGSHConfiguration.cs @@ -35,6 +35,9 @@ namespace MarketData.Generator.MGSHMomentum // Manage buying and selling public bool KeepSlotPositions{get;set;} // if this setting is true then we never sell slot positions, allowing the trailing stop to eventually invoke a sell. +// Pricing Exceptions + public int MaxPricingExceptions{get;set;} // This is the pricing exception limit. If we have this many of exceptions then we will sell the security at the last known good price. + // Fundamental screenings public double MarketCapLowerLimit{get;set;} @@ -121,6 +124,8 @@ namespace MarketData.Generator.MGSHMomentum // Manage buying and selling KeepSlotPositions=true; // The default is true to retain legacy functionality + MaxPricingExceptions=3; // The maximum number of pricing exceptions. If exceeded then the security will be sold at the last known good price + // Other settings UseEBITDAScreen=true; // true is the default UseRevenuePerShareScreen=true; // true is the default @@ -203,6 +208,8 @@ namespace MarketData.Generator.MGSHMomentum nvpCollection.Add(new NVP("HedgeCloseAboveSMANDays",HedgeCloseAboveSMANDays.ToString())); nvpCollection.Add(new NVP("HedgeBandBreakCheckDays",HedgeBandBreakCheckDays.ToString())); + nvpCollection.Add(new NVP("MaxPricingExceptions",MaxPricingExceptions.ToString())); + return nvpCollection; } public static MGSHConfiguration FromNVPCollection(NVPCollection nvpCollection) @@ -240,6 +247,7 @@ namespace MarketData.Generator.MGSHMomentum mgConfiguration.UseFallbackCandidate=nvpDictionary["UseFallbackCandidate"].Get(); mgConfiguration.FallbackCandidate=nvpDictionary["FallbackCandidate"].Get(); mgConfiguration.FallbackCandidateBestOf=nvpDictionary["FallbackCandidateBestOf"].Get(); + mgConfiguration.MaxPricingExceptions=nvpDictionary["MaxPricingExceptions"].Get(); if(nvpDictionary.ContainsKey("QualityIndicatorType")) mgConfiguration.QualityIndicatorType=nvpDictionary["QualityIndicatorType"].Get(); else mgConfiguration.QualityIndicatorType=MGSHQualityIndicator.ToString(MGSHQualityIndicator.QualityType.IDIndicator); @@ -283,6 +291,8 @@ namespace MarketData.Generator.MGSHMomentum public void DisplayConfiguration() { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Verbose,{0}",Verbose)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MaxPricingExceptions,{0}",MaxPricingExceptions)); + MDTrace.WriteLine(LogLevel.DEBUG,String.Format("KeepSlotPositions,{0}",KeepSlotPositions)); MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Holding Period,{0}",HoldingPeriod)); MDTrace.WriteLine(LogLevel.DEBUG,String.Format("MaxPositions,{0}",MaxPositions)); diff --git a/MarketDataLib/Generator/MGSHMomentum/MGSHPositions.cs b/MarketDataLib/Generator/MGSHMomentum/MGSHPositions.cs index bd63ee6..b30d2b3 100644 --- a/MarketDataLib/Generator/MGSHMomentum/MGSHPositions.cs +++ b/MarketDataLib/Generator/MGSHMomentum/MGSHPositions.cs @@ -2,10 +2,11 @@ using System.Collections.Generic; using MarketData.Utils; using System.Linq; +using MarketData.Generator.Interface; namespace MarketData.Generator.MGSHMomentum { - public class MGSHPosition + public class MGSHPosition : IPosition { public MGSHPosition() { @@ -33,9 +34,40 @@ namespace MarketData.Generator.MGSHMomentum InitialStopLimit = position.InitialStopLimit; TrailingStopLimit = position.TrailingStopLimit; LastStopAdjustment = position.LastStopAdjustment; + PositionRiskPercentDecimal = position.PositionRiskPercentDecimal; R = position.R; if(null!=position.Comment)Comment=string.Copy(position.Comment); } + + public static MGSHPosition Clone(MGSHPosition positionToClone) + { + MGSHPosition position = new MGSHPosition(); + position.Symbol = positionToClone.Symbol; + position.PurchaseDate = positionToClone.PurchaseDate; + position.SellDate=positionToClone.SellDate; + position.Shares=positionToClone.Shares; + position.PurchasePrice=positionToClone.PurchasePrice; + position.CurrentPrice=positionToClone.CurrentPrice; + position.Volume=positionToClone.Volume; + position.Return1D=positionToClone.Return1D; + position.ZacksRank=positionToClone.ZacksRank; + position.CumReturn252=positionToClone.CumReturn252; + position.IDIndicator=positionToClone.IDIndicator; + position.Score=positionToClone.Score; + position.MaxDrawdown=positionToClone.MaxDrawdown; + position.MaxUpside=positionToClone.MaxUpside; + position.Velocity=positionToClone.Velocity; + position.PE=positionToClone.PE; + position.Beta=positionToClone.Beta; + position.InitialStopLimit=positionToClone.InitialStopLimit; + position.TrailingStopLimit=positionToClone.TrailingStopLimit; + position.LastStopAdjustment=positionToClone.LastStopAdjustment; + position.PositionRiskPercentDecimal=positionToClone.PositionRiskPercentDecimal; + position.R=positionToClone.R; + position.Comment=positionToClone.Comment; + return position; + } + public String Symbol{get;set;} public DateTime PurchaseDate{get;set;} public DateTime SellDate{get;set;} @@ -60,7 +92,8 @@ namespace MarketData.Generator.MGSHMomentum public double InitialStopLimit {get; set; } public double TrailingStopLimit {get; set; } public DateTime LastStopAdjustment {get; set; } - public double R {get; set; } + public double PositionRiskPercentDecimal {get; set; } + public double R { get; set; } public String Comment {get; set; } public virtual NVPCollection ToNVPCollection() @@ -86,6 +119,7 @@ namespace MarketData.Generator.MGSHMomentum nvpCollection.Add(new NVP("InitialStopLimit", InitialStopLimit.ToString())); nvpCollection.Add(new NVP("TrailingStopLimit", TrailingStopLimit.ToString())); nvpCollection.Add(new NVP("LastStopAdjustment", LastStopAdjustment.ToString())); + nvpCollection.Add(new NVP("PositionRiskPercentDecimal", PositionRiskPercentDecimal.ToString())); nvpCollection.Add(new NVP("R", R.ToString())); nvpCollection.Add(new NVP("Comment", Comment?.ToString())); return nvpCollection; @@ -121,6 +155,8 @@ namespace MarketData.Generator.MGSHMomentum else position.LastStopAdjustment=Utility.Epoch; if(nvpDictionary.ContainsKey("Comment")) position.Comment=nvpDictionary["Comment"].Get(); else position.Comment=null; + if(nvpDictionary.ContainsKey("PositionRiskPercentDecimal")) position.PositionRiskPercentDecimal=nvpDictionary["PositionRiskPercentDecimal"].Get(); + else position.PositionRiskPercentDecimal=double.NaN; if(nvpDictionary.ContainsKey("R")) position.R=nvpDictionary["R"].Get(); else position.R=double.NaN; return position; diff --git a/MarketDataLib/Generator/MGSHMomentum/MGSHSessionManager.cs b/MarketDataLib/Generator/MGSHMomentum/MGSHSessionManager.cs index 24a25d8..6596de5 100644 --- a/MarketDataLib/Generator/MGSHMomentum/MGSHSessionManager.cs +++ b/MarketDataLib/Generator/MGSHMomentum/MGSHSessionManager.cs @@ -64,6 +64,11 @@ namespace MarketData.Generator.MGSHMomentum streamWriter.WriteLine((new NVP("TotalHedgePositions",nvpHedgePositionsStringList.Count.ToString())).ToString()); foreach(String str in nvpHedgePositionsStringList)streamWriter.WriteLine(str); + NVPCollections pricingExceptionsCollections=sessionParams.PricingExceptions.ToNVPCollections(); + List nvpPricingExceptionsCollectionsStringList=pricingExceptionsCollections.ToList(); + streamWriter.WriteLine((new NVP("TotalPricingExceptions",nvpPricingExceptionsCollectionsStringList.Count.ToString())).ToString()); + foreach(String str in nvpPricingExceptionsCollectionsStringList)streamWriter.WriteLine(str); + streamWriter.Flush(); outStream.Flush(); streamWriter.Close(); @@ -126,6 +131,7 @@ namespace MarketData.Generator.MGSHMomentum if(2.00==version) { + // stop limits int totalStopLimits=(new NVP(streamReader.ReadLine())).Get(); sessionParams.StopLimits=new StopLimits(); for(int stopLimitIndex=0;stopLimitIndex(); NVPCollections nvpHedgeCollections=new NVPCollections(); for(int hedgePositionIndex=0;hedgePositionIndex(); + NVPCollections nvpPricingExceptionsCollections=new NVPCollections(); + for(int pricingExceptionIndex=0;pricingExceptionIndex +/// This StopLimit class is used by models and the UI +/// + namespace MarketData.Generator.Model { public class StopLimits:List @@ -53,6 +57,7 @@ namespace MarketData.Generator.Model 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) { @@ -71,15 +76,17 @@ namespace MarketData.Generator.Model 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("Symbol,AnalysisDate,PreviousStop,NewStop,CurrentPriceLow,CurrentPriceClose,PriceTrendIndicatorSlope"); + 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(","); @@ -99,6 +106,7 @@ namespace MarketData.Generator.Model 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) @@ -113,6 +121,7 @@ namespace MarketData.Generator.Model stopLimit.CurrentPriceLow=nvpDictionary["CurrentPriceLow"].Get(); stopLimit.CurrentPriceClose=nvpDictionary["CurrentPriceClose"].Get(); stopLimit.PriceTrendIndicatorSlope=nvpDictionary["PriceTrendIndicatorSlope"].Get(); + if(nvpDictionary.ContainsKey("StopLimitId"))stopLimit.StopLimitId=nvpDictionary["StopLimitId"].Get(); return stopLimit; } } diff --git a/MarketDataLib/MarketDataLib.csproj b/MarketDataLib/MarketDataLib.csproj index eb22b3a..9ea8269 100644 --- a/MarketDataLib/MarketDataLib.csproj +++ b/MarketDataLib/MarketDataLib.csproj @@ -138,6 +138,7 @@ + @@ -149,6 +150,7 @@ + diff --git a/MarketDataLib/MarketDataModel/PortfolioTrade.cs b/MarketDataLib/MarketDataModel/PortfolioTrade.cs index 399f4f5..f324811 100644 --- a/MarketDataLib/MarketDataModel/PortfolioTrade.cs +++ b/MarketDataLib/MarketDataModel/PortfolioTrade.cs @@ -55,67 +55,118 @@ namespace MarketData.MarketDataModel get {return tradeId ;} set { tradeId = value; ;} } + public String Symbol { get { return symbol; } set { symbol = value; } } + public DateTime TradeDate { get { return tradeDate; } set { tradeDate = value; } } + public double Shares { get { return shares; } set { shares = value; } } + public double Exposure() { return Price*Shares; } + public String BuySell { get { return buySell; } set { buySell = value; } } + public String Status { get { return status; } set { status = value; } } + public bool IsOpen { get { return status.ToUpper().Equals("OPEN"); } } + public bool IsClosed { get { return !IsOpen; } } + public String Account { get { return account; } set{account=value;} } + public DateTime SellDate { get { return sellDate; } set { sellDate = value; } } + public double SellPrice { get { return sellPrice;} set { sellPrice = value;} } + public double Price { get { return price; } set { price = value; } } + public double Commission { get { return commission; } set { commission = value; } } + + public virtual NVPCollection ToNVPCollection() + { + NVPCollection nvpCollection=new NVPCollection(); + nvpCollection.Add(new NVP("TradeId",TradeId.ToString())); + if(null!=Symbol)nvpCollection.Add(new NVP("Symbol",Symbol.ToString())); + nvpCollection.Add(new NVP("TradeDate",TradeDate.ToString())); + nvpCollection.Add(new NVP("Shares",Shares.ToString())); + nvpCollection.Add(new NVP("Commission",Commission.ToString())); + if(null!=BuySell)nvpCollection.Add(new NVP("BuySell",BuySell.ToString())); + if(null!=Account)nvpCollection.Add(new NVP("Account",Account.ToString())); + if(null!=Status)nvpCollection.Add(new NVP("Status",Status.ToString())); + nvpCollection.Add(new NVP("SellPrice",SellPrice.ToString())); + nvpCollection.Add(new NVP("SellDate",SellDate.ToString())); + nvpCollection.Add(new NVP("Price",Price.ToString())); + return nvpCollection; + } + + public static PortfolioTrade FromNVPCollection(NVPCollection nvpCollection) + { + PortfolioTrade portfolioTrade=new PortfolioTrade(); + + NVPDictionary nvpDictionary=nvpCollection.ToDictionary(); + portfolioTrade.TradeId=nvpDictionary["TradeId"].Get(); + portfolioTrade.Symbol=nvpDictionary["Symbol"].Get(); + portfolioTrade.TradeDate=nvpDictionary["TradeDate"].Get(); + portfolioTrade.Shares=nvpDictionary["Shares"].Get(); + portfolioTrade.Commission=nvpDictionary["Commission"].Get(); + if(nvpDictionary.ContainsKey("BuySell"))portfolioTrade.BuySell=nvpDictionary["BuySell"].Get(); + if(nvpDictionary.ContainsKey("Account"))portfolioTrade.Account=nvpDictionary["Account"].Get(); + if(nvpDictionary.ContainsKey("Status"))portfolioTrade.Status=nvpDictionary["Status"].Get(); + portfolioTrade.SellPrice=nvpDictionary["SellPrice"].Get(); + portfolioTrade.SellDate=nvpDictionary["SellDate"].Get(); + portfolioTrade.Price=nvpDictionary["Price"].Get(); + return portfolioTrade; + } + + } } diff --git a/MarketDataLib/MarketDataModel/PortfolioTrades.cs b/MarketDataLib/MarketDataModel/PortfolioTrades.cs index 21b31f1..5742933 100644 --- a/MarketDataLib/MarketDataModel/PortfolioTrades.cs +++ b/MarketDataLib/MarketDataModel/PortfolioTrades.cs @@ -99,6 +99,7 @@ namespace MarketData.MarketDataModel { return this.Min(x => x.TradeDate); } + public DateTime GetMinTradeDate(String symbol) { DateTime minDate=Utility.Epoch; @@ -271,5 +272,26 @@ namespace MarketData.MarketDataModel if(!portfolioTrade.SellDate.Equals(Utility.Epoch)&&price.Date>portfolioTrade.SellDate) return null; return portfolioTrade.Shares*portfolioTrade.Price; } + + // Collections + + public NVPCollections ToNVPCollections() + { + NVPCollections nvpCollections=new NVPCollections(); + foreach(PortfolioTrade portfolioTrade in this) + { + nvpCollections.Add(portfolioTrade.ToNVPCollection()); + } + return nvpCollections; + } + public static PortfolioTrades FromNVPCollections(NVPCollections nvpCollections) + { + PortfolioTrades portfolioTrades=new PortfolioTrades(); + foreach(NVPCollection nvpCollection in nvpCollections) + { + portfolioTrades.Add(PortfolioTrade.FromNVPCollection(nvpCollection)); + } + return portfolioTrades; + } } } \ No newline at end of file diff --git a/MarketDataLib/MarketDataModel/StopLimit.cs b/MarketDataLib/MarketDataModel/StopLimit.cs index 8e86f6f..b754de3 100644 --- a/MarketDataLib/MarketDataModel/StopLimit.cs +++ b/MarketDataLib/MarketDataModel/StopLimit.cs @@ -3,6 +3,10 @@ using System; using System.Collections.Generic; using System.Text; +/// +/// This StopLimit class is used for database persistence +/// + namespace MarketData.MarketDataModel { public class StopLimitConstants @@ -102,4 +106,4 @@ namespace MarketData.MarketDataModel return stopLimit; } } -} \ No newline at end of file +} diff --git a/MarketDataLib/Utility/DateGenerator.cs b/MarketDataLib/Utility/DateGenerator.cs index f6a7873..75f99f4 100644 --- a/MarketDataLib/Utility/DateGenerator.cs +++ b/MarketDataLib/Utility/DateGenerator.cs @@ -1,8 +1,5 @@ using System; -using System.Collections; using System.Collections.Generic; -using MarketData.Utils; -using MarketData.DataAccess; // Filename: DateGenerator.cs // Author:Sean Kessler @@ -10,6 +7,7 @@ using MarketData.DataAccess; namespace MarketData.Utils { /// DateGenerator - Generate Historical Dates +/// "BusinessDay" is analagous to "MarketDay". To qualify as a business day the date must not be a weekend or a market holiday [Serializable] public class DateGenerator { @@ -19,6 +17,8 @@ namespace MarketData.Utils public DateGenerator() { } +/// GetCurrMonthStart - Finds the first business day of the previous month +/// DateTime public DateTime GetPrevMonthStart(DateTime dateTime) { DateTime startDate = new DateTime(dateTime.Year, dateTime.Month, 1); @@ -26,6 +26,8 @@ namespace MarketData.Utils startDate = GetNextBusinessDay(startDate); return startDate; } +/// GetCurrMonthStart - Finds the first business day of the current month +/// DateTime public DateTime GetCurrMonthStart(DateTime dateTime) { DateTime startDate = new DateTime(dateTime.Year, dateTime.Month, 1); @@ -46,7 +48,7 @@ namespace MarketData.Utils while(IsWeekend(asOf)||IsHoliday(asOf))asOf=asOf.Subtract(oneDay); return asOf.Date; } - /// FindNextBusinessDays - Finds following business day + /// FindForwardBusinessDay - Finds following business day going daysForward days /// DateTime public DateTime FindForwardBusinessDay(DateTime asOf,int daysForward) { @@ -56,6 +58,17 @@ namespace MarketData.Utils } return asOf.Date; } + /// FindPastBusinessDay - Finds previous business day going daysPast days + /// DateTime + public DateTime FindPastBusinessDay(DateTime asOf,int daysPast) + { + for (int index = 0; index < daysPast; index++) + { + asOf = FindPrevBusinessDay(asOf); + } + return asOf.Date; + } + /// FindNextBusinessDay - Finds following nth business day /// DateTime public DateTime FindNextBusinessDay(DateTime asOf,int days) @@ -77,6 +90,7 @@ namespace MarketData.Utils while(IsWeekend(asOf)||IsHoliday(asOf))asOf=asOf.Add(oneDay); return asOf.Date; } + /// GetPrevBusinessDay - Gets previous business day /// If the given date is a business day then this method will return the given date /// None @@ -86,6 +100,7 @@ namespace MarketData.Utils while(IsWeekend(asOf)||IsHoliday(asOf))asOf=asOf.Subtract(oneDay); return asOf.Date; } + /// GetNextBusinessDay - Gets next business day /// If the given date is a business day then this method will return the given date /// None @@ -95,6 +110,7 @@ namespace MarketData.Utils while(IsWeekend(asOf)||IsHoliday(asOf))asOf=asOf.Add(oneDay); return asOf.Date; } + /// GetNextDay - Gets next business day /// If the given date is a business day then this method will return the given date /// None @@ -102,6 +118,7 @@ namespace MarketData.Utils { return (asOf.Add(Utility.OneDay)).Date; } + /// GetPrevFriday - Gets date of prior friday /// Get the date of the previous friday - if previous Friday is holiday will seek previous business day /// None @@ -124,6 +141,7 @@ namespace MarketData.Utils } throw new Exception("The week no longer contains Friday?"); } + public DateTime GetPrevQuarterStartDate(DateTime asOf) { int month=asOf.Month; @@ -136,6 +154,7 @@ namespace MarketData.Utils quarterStartDate=GetNextBusinessDay(quarterStartDate); return quarterStartDate; } + public DateTime GetNextQuarterStartDate(DateTime asOf) { DateTime prevQuarterStartDate=GetPrevQuarterStartDate(asOf); @@ -148,6 +167,7 @@ namespace MarketData.Utils nextQuarterStartDate=GetNextBusinessDay(nextQuarterStartDate); return nextQuarterStartDate; } + public DateTime GetCurrentMonthEnd(DateTime asOf) { TimeSpan oneDay=new TimeSpan(-1,0,0,0); @@ -156,6 +176,7 @@ namespace MarketData.Utils while(IsWeekend(date)||IsHoliday(date))date=date.Add(oneDay); return date; } + public DateTime GetNextMonthEnd(DateTime asOf) { TimeSpan oneDay=new TimeSpan(-1,0,0,0); @@ -165,6 +186,7 @@ namespace MarketData.Utils while(IsWeekend(date)||IsHoliday(date))date=date.Add(oneDay); return date; } + public DateTime GetPrevMonthEnd(DateTime asOf) { TimeSpan oneDay=new TimeSpan(-1,0,0,0); @@ -174,6 +196,7 @@ namespace MarketData.Utils while(IsWeekend(date)||IsHoliday(date))date=date.Add(oneDay); return date; } + public DateTime GetPrevMonthEnd(DateTime asOf,int count) { for(int index=0;index endDate) @@ -276,10 +307,12 @@ namespace MarketData.Utils return (int)timeSpan.TotalDays; } } + public int MonthsBetween(DateTime startDate,DateTime endDate) { return DaysBetween(startDate,endDate)/30; } + public static List GenerateHistoricalYear(int startYear,int years) { List yearsList = new List(); @@ -290,6 +323,7 @@ namespace MarketData.Utils } return yearsList; } + public List GenerateHistoricalDates(DateTime startDate, int dayCount) { List histDates=new List(); @@ -308,11 +342,13 @@ namespace MarketData.Utils } return histDates; } + public int TradingDaysBetween(DateTime startDate,DateTime endDate) { List historicalDates=GenerateHistoricalDates(startDate,endDate); return historicalDates.Count; } + // The function will figure out which date is the most recent and which one is the historical date. // Generally, make startDate the most recent and endDate the historical date. // The returned series will contain an ascending date series with the earliest (most distant) date in the lowest numbered index. @@ -343,6 +379,7 @@ namespace MarketData.Utils if (reverse) histDates.Reverse(); return histDates; } + // The function will figure out which date is the most recent and which one is the historical date. // Generally, make startDate the most recent and endDate the historical date. public List GenerateHistoricalDatesActual(DateTime startDate, DateTime endDate) @@ -372,17 +409,20 @@ namespace MarketData.Utils if (reverse) histDates.Reverse(); return histDates; } + public bool IsMarketOpen(DateTime dateTime) { if(IsWeekend(dateTime)||IsHoliday(dateTime))return false; return true; } + public bool IsWeekend(DateTime dateTime) { if(DayOfWeek.Sunday==dateTime.DayOfWeek || DayOfWeek.Saturday==dateTime.DayOfWeek)return true; return false; } -// make this access a singleton cache. + +// This uses a singleton cache of holidays. public bool IsHoliday(DateTime dateTime) { return HolidayCache.GetInstance().IsHoliday(dateTime); diff --git a/MarketDataLib/Utility/Utility.cs b/MarketDataLib/Utility/Utility.cs index 7238345..38f6ecc 100644 --- a/MarketDataLib/Utility/Utility.cs +++ b/MarketDataLib/Utility/Utility.cs @@ -231,6 +231,7 @@ namespace MarketData.Utils } return sb.ToString(); } + public static String GetPath(String strPathFileName) { int index=strPathFileName.LastIndexOf('\\'); @@ -238,6 +239,15 @@ namespace MarketData.Utils String strPath = strPathFileName.Substring(0, index); return strPath; } + + public static String GetFileNameNoExtension(String pathFileName) + { + if(null==pathFileName)return null; + pathFileName = Path.GetFileName(pathFileName); + pathFileName=Utility.BetweenString(pathFileName,null,"."); + return pathFileName; + } + public static String KeepBefore(String strItem,String strKeepBefore) { int startPos=strItem.IndexOf(strKeepBefore); diff --git a/Program.cs b/Program.cs index e11848a..fadc806 100644 --- a/Program.cs +++ b/Program.cs @@ -982,43 +982,6 @@ namespace MarketData Trace.Listeners.Add(new TextWriterTraceListener(strLogFile)); DateTime currentDate=DateTime.Now; - //int hitCount =0; - - //CompanyProfiles companyProfiles = CompanyProfileDA.GetCompanyProfiles(); - //List symbols = companyProfiles.Select(x => x.Symbol).Where(x => !x.StartsWith("^")).ToList(); - //foreach (String symbol in symbols) - //{ - // CompanyProfile companyProfile = MarketDataHelper.GetCompanyProfileYahoo(symbol); - // if (null != companyProfile) - // { - // MDTrace.WriteLine(LogLevel.DEBUG, $"Hits: {hitCount} Symbol:{symbol} Sector:{companyProfile.Sector} Industry:{companyProfile.Industry} Description:{companyProfile.Description}"); - // MDTrace.WriteLine(LogLevel.DEBUG, ""); - // hitCount++; - // } - // Thread.Sleep(1000); - //} - - - - - - - // CompanyProfile companyProfile = MarketDataHelper.GetCompanyProfile("MOD"); - //PortfolioTrades portfolioTrades = PortfolioDA.GetOpenTrades(); - //List symbols = portfolioTrades.Select(x => x.Symbol).Distinct().ToList(); - //for (int rindex=0; rindexmaxHolidayDate) {