Add the CMMomentum model

This commit is contained in:
2025-04-24 16:51:09 -04:00
parent ddff565458
commit 51a207d690
15 changed files with 2438 additions and 3 deletions

View File

@@ -0,0 +1,131 @@
using MarketData.Generator.CMMomentum;
using MarketData.Utils;
using System;
using System.Collections.Generic;
namespace MarketData
{
public static class CMMomentumHelper
{
/// <summary>
/// CMCANDIDATELASTRESORT /TRADEDATE:
/// </summary>
/// <param name="args"></param>
public static void RunCMCandidateLastResort(CommandArgs commandArgs)
{
CMParams cmParams = new CMParams();
List<String> candidates = Utility.ToList(cmParams.FallbackCandidateBestOf);
if (!commandArgs.Has("TRADEDATE")) { MDTrace.WriteLine(LogLevel.DEBUG, "TRADEDATE required"); return; }
CMCandidate cmCandidate = CMCandidateGenerator.GetFallbackCandidateOfLastResort(candidates, commandArgs.Coalesce<DateTime>("TRADEDATE"), commandArgs.Coalesce<DateTime>("TRADEDATE"), cmParams);
if (null == cmCandidate) { MDTrace.WriteLine(LogLevel.DEBUG, "Unable to determine candidate of last resort."); return; }
foreach (String candidate in candidates)
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Candidate examined..{0}", candidate));
}
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Best candidate is {0}", cmCandidate.Symbol));
}
/// <summary>
/// CMGAINLOSS /SESSIONFILE:{PATHSESSIONFILE} (i.e.) CMGAINLOSS /SESSIONFILE:C:\boneyard\marketdata\bin\Debug\saferun\CM20191031.txt");
/// </summary>
/// <param name="args"></param>
public static void RunCMGainLoss(CommandArgs commandArgs)
{
if(!commandArgs.Has("SESSIONFILE")) { MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Missing SESSIONFILE")); return; }
CMMomentumBacktest cmBacktest=new CMMomentumBacktest();
cmBacktest.DisplayGainLoss(commandArgs.Coalesce<String>("SESSIONFILE"));
}
/// <summary>
/// CMSESSION /SESSIONFILE:
/// </summary>
/// <param name="args"></param>
public static void RunCMSession(CommandArgs commandArgs)
{
if (!commandArgs.Has("SESSIONFILE"))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Missing SESSIONFILE"));
return;
}
CMMomentumBacktest cmBacktest = new CMMomentumBacktest();
cmBacktest.DisplaySession(commandArgs.Coalesce<String>("SESSIONFILE"));
}
/// <summary>
/// RUNCMBACKTEST /STARTDATE: /MAXPOSITIONS: /INITIALCASH: /HOLDINGPERIOD: /{USEBINBASEDPOSITIONSIZING}: /{USEBINBASEDPOSITIONSIZINGNUMBINS}: /{TARGETBETA}: /{ENDDATE}: /SESSIONFILE: /{USECNN}: /{USECNNHOST}: /{USECNNDAYCOUNT}:
/// </summary>
/// <param name="args"></param>
public static void RunCMMomentum(CommandArgs commandArgs)
{
if(!commandArgs.Has("STARTDATE,MAXPOSITIONS,INITIALCASH,HOLDINGPERIOD"))
{
if(!commandArgs.Has("STARTDATE")) MDTrace.WriteLine(LogLevel.DEBUG,"Missing STARTDATE");
if(!commandArgs.Has("MAXPOSITIONS")) MDTrace.WriteLine(LogLevel.DEBUG,"Missing MAXPOSITIONS");
if(!commandArgs.Has("INITIALCASH")) MDTrace.WriteLine(LogLevel.DEBUG,"Missing INITIALCASH");
if(!commandArgs.Has("HOLDINGPERIOD")) MDTrace.WriteLine(LogLevel.DEBUG,"Missing HOLDINGPERIOD");
return;
}
CMParams cmParams=new CMParams();
cmParams.AnalysisDate=commandArgs.Get<DateTime>("STARTDATE");
cmParams.MaxPositions=commandArgs.Get<int>("MAXPOSITIONS");
cmParams.InitialCash=commandArgs.Get<double>("INITIALCASH");
cmParams.HoldingPeriod=commandArgs.Get<int>("HOLDINGPERIOD");
if(commandArgs.Has("USECNN"))
{
if(!commandArgs.Has("USECNNCLIENT,USECNNDAYCOUNT"))
{
MDTrace.WriteLine(LogLevel.DEBUG,"Missing USECNNCLIENT, USECNNDAYCOUNT");
return;
}
cmParams.UseCNN=true;
cmParams.UseCNNHost=commandArgs.Get<String>("USECNNHOST");
cmParams.UseCNNDayCount=commandArgs.Get<int>("USECNNDAYCOUNT");
if(commandArgs.Has("USECNNREWARDPERCENTDECIMAL"))cmParams.UseCNNRewardPercentDecimal=commandArgs.Get<double>("USECNNREWARDPERCENTDECIMAL");
}
if(commandArgs.Has("USEOVEREXTENDEDINDICATOR"))
{
if(!commandArgs.Has("USEOVEREXTENDEDINDICATORDAYS,USEOVEREXTENDEDINDICATORVIOLATIONTHRESHHOLD,USEOVEREXTENDEDINDICATORMARGINPERCENT"))
{
MDTrace.WriteLine(LogLevel.DEBUG,"Missing USEOVEREXTENDEDINDICATORDAYS, USEOVEREXTENDEDINDICATORVIOLATIONTHRESHHOLD, USEOVEREXTENDEDINDICATORMARGINPERCENT");
return;
}
cmParams.UseOverExtendedIndicator=commandArgs.Get<bool>("USEOVEREXTENDEDINDICATOR");
cmParams.UseOverExtendedIndicatorDays=commandArgs.Get<int>("USEOVEREXTENDEDINDICATORDAYS");
cmParams.UseOverExtendedIndicatorViolationThreshhold=commandArgs.Get<int>("USEOVEREXTENDEDINDICATORVIOLATIONTHRESHHOLD");
cmParams.UseOverExtendedIndicatorMarginPercent=commandArgs.Get<double>("USEOVEREXTENDEDINDICATORMARGINPERCENT");
}
if(commandArgs.Has("USEMAXPOSITIONBUCKETWEIGHT")) // UseMaxPositionBucketWeight
{
if(!commandArgs.Has("USEMAXPOSITIONBUCKETWEIGHTMAXWEIGHT")) // UseMaxPositionBucketWeightMaxWeight
{
MDTrace.WriteLine(LogLevel.DEBUG,"Missing USEMAXPOSITIONBUCKETWEIGHTMAXWEIGHT");
return;
}
cmParams.UseMaxPositionBucketWeight=commandArgs.Get<bool>("USEMAXPOSITIONBUCKETWEIGHT");
cmParams.UseMaxPositionBucketWeightMaxWeight=commandArgs.Get<double>("USEMAXPOSITIONBUCKETWEIGHTMAXWEIGHT");
}
else
{
cmParams.UseMaxPositionBucketWeight=false;
cmParams.UseMaxPositionBucketWeightMaxWeight=0;
}
if(commandArgs.Has("TARGETBETA"))cmParams.TargetBeta=commandArgs.Get<double>("TARGETBETA");
else cmParams.TargetBeta=1.00;
DateTime endDate=DateTime.Now;
String pathSessionFileName=commandArgs.Coalesce<String>("SESSIONFILE",null);
if(null!=pathSessionFileName) pathSessionFileName=pathSessionFileName.Trim();
cmParams.DisplayHeader();
CMMomentumBacktest backtestMomentum=new CMMomentumBacktest();
List<CMBacktestResult> results=new List<CMBacktestResult>();
results.Add(backtestMomentum.PerformBacktest(cmParams.AnalysisDate,endDate,pathSessionFileName,cmParams));
}
}
}

View File

@@ -4,7 +4,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
namespace MarketData
namespace MarketData.ModelHelper
{
/// <summary>
/// This model is based on Mark Minervini

View File

@@ -4,7 +4,7 @@ using System;
using System.Collections.Generic;
using System.IO;
namespace MarketData
namespace MarketData.ModelHelper
{
public static class MGSHMomentumHelper
{

View File

@@ -9,6 +9,7 @@ using MarketData.Helper;
using MarketData.Integration;
using MarketData.Cache;
using MarketData.Generator;
using MarketData.ModelHelper;
namespace MarketData.Services
{
@@ -43,6 +44,10 @@ namespace MarketData.Services
MDTrace.WriteLine(LogLevel.DEBUG,"MGSHRUNDAILY /SESSIONFILE: /TRADEDATE:");
MDTrace.WriteLine(LogLevel.DEBUG,"RUNCMTREND /MODE:DAILY|BACKTEST|RUNTRENDTEMPLATE|ENTRYTEST /SYMBOL:{for mode ANALYZE,ENTRYTEST} /TRADEDATE:{for mode DAILY,RUNTRENDTEMPLATE) /STARTDATE:(for mode BACKTEST,ENTRYTEST) /ENDDATE:(for mode BACKTEST) /INITIALCASH: /SESSIONFILE: /MAXOPENPOSITIONS: /MAXDAILYPOSITIONS: Runs Mark Minervini trend");
MDTrace.WriteLine(LogLevel.DEBUG,"CMTSESSION /SESSIONFILE:{pathfilename} Runs Mark Minervini trend display session");
MDTrace.WriteLine(LogLevel.DEBUG, "RUNCMBACKTEST /STARTDATE: /MAXPOSITIONS: /INITIALCASH: /HOLDINGPERIOD: /{USEBINBASEDPOSITIONSIZING}: /{USEBINBASEDPOSITIONSIZINGNUMBINS}: /{TARGETBETA}: /{ENDDATE}: /SESSIONFILE: /{USECNN}: /{USECNNHOST}: /{USECNNDAYCOUNT}:");
MDTrace.WriteLine(LogLevel.DEBUG, "CMSESSION /SESSIONFILE:");
MDTrace.WriteLine(LogLevel.DEBUG, "CMCANDIDATELASTRESORT /TRADEDATE:");
MDTrace.WriteLine(LogLevel.DEBUG, @"CMGAINLOSS /SESSIONFILE:{PATHSESSIONFILE} (i.e.) CMGAINLOSS /SESSIONFILE:C:\boneyard\marketdata\bin\Debug\saferun\CM20191031.txt");
MDTrace.WriteLine(LogLevel.DEBUG,"ECHO {param1} {param2} {param(n)");
}
@@ -77,6 +82,10 @@ namespace MarketData.Services
tasks.Add("MGSHRUNDAILY",TaskMGSHRunDaily);
tasks.Add("RUNCMTREND",TaskRunCMTrend);
tasks.Add("CMTSESSION",TaskCMTSession);
tasks.Add("RUNCMBACKTEST",TaskCMMRunCMBacktest);
tasks.Add("CMSESSION",TaskCMMRunCMSession);
tasks.Add("CMCANDIDATELASTRESORT",TaskCMMRunCMCandidateLastResort);
tasks.Add("CMGAINLOSS",TaskCMMRunCMGainLoss);
tasks.Add("ECHO",TaskEcho);
GlobalConfig.Instance.Configuration = configuration; // This call sets up configuration stuff so it needs to be first.
@@ -319,7 +328,32 @@ namespace MarketData.Services
{
CMTrendHelper.HandleCMTSession(commandArgs);
await Task.FromResult(true);
}
}
public async Task TaskCMMRunCMBacktest(CommandArgs commandArgs)
{
CMMomentumHelper.RunCMMomentum(commandArgs);
await Task.FromResult(true);
}
public async Task TaskCMMRunCMSession(CommandArgs commandArgs)
{
CMMomentumHelper.RunCMSession(commandArgs);
await Task.FromResult(true);
}
public async Task TaskCMMRunCMCandidateLastResort(CommandArgs commandArgs)
{
CMMomentumHelper.RunCMCandidateLastResort(commandArgs);
await Task.FromResult(true);
}
public async Task TaskCMMRunCMGainLoss(CommandArgs commandArgs)
{
CMMomentumHelper.RunCMGainLoss(commandArgs);
await Task.FromResult(true);
}
// *********************************************************************************************************************************************************
// ******************************************************************* E N D T A S K S ********************************************************************

View File

@@ -0,0 +1,144 @@
using MarketData.Utils;
namespace MarketData.Generator.CMMomentum
{
public class ActivePositions : Dictionary<int, Positions>
{
public ActivePositions()
{
}
public double GetExposure()
{
int count = Count;
double exposure = 0.00;
for (int slotIndex = 0; slotIndex < count; slotIndex++)
{
List<Position> positions = this[slotIndex];
if (null == positions || 0 == positions.Count) continue;
exposure += (from Position position in positions select position.Exposure).Sum();
}
return exposure;
}
public bool Remove(Position searchPosition)
{
List<int> keys = new List<int>(this.Keys);
for (int index = 0; index < keys.Count; index++)
{
Positions positions = this[keys[index]];
foreach (Position slotPosition in positions)
{
if (slotPosition == searchPosition)
{
positions.Remove(searchPosition);
return true;
}
}
}
return false;
}
public Positions GetPositions()
{
Positions positionsCollection=new Positions();
List<int> keys=new List<int>(this.Keys);
Dictionary<String,String> symbols=new Dictionary<String,String>();
for(int index=0;index<keys.Count;index++)
{
Positions positions=this[keys[index]];
foreach(Position position in positions)
{
positionsCollection.Add(position);
}
}
return positionsCollection;
}
public List<String> GetSymbols()
{
Dictionary<String, String> symbols = new Dictionary<String, String>();
for (int slotIndex = 0; slotIndex < Count; slotIndex++)
{
List<Position> positions = this[slotIndex];
if (null == positions || 0 == positions.Count) continue;
for (int positionIndex = 0; positionIndex < positions.Count; positionIndex++)
{
Position position = positions[positionIndex];
if (!symbols.ContainsKey(position.Symbol)) symbols.Add(position.Symbol, position.Symbol);
}
}
return new List<String>(symbols.Keys);
}
public double GetMarketValue()
{
int count = Count;
double marketValue = 0.00;
for (int slotIndex = 0; slotIndex < count; slotIndex++)
{
List<Position> positions = this[slotIndex];
if (null == positions || 0 == positions.Count) continue;
marketValue += (from Position position in positions select position.MarketValue).Sum();
}
return marketValue;
}
public double GetGainLoss()
{
int count = Count;
double marketValue = 0.00;
double exposure = 0.00;
for (int slotIndex = 0; slotIndex < count; slotIndex++)
{
List<Position> positions = this[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();
}
return marketValue - exposure;
}
public double GetGainLossPercent()
{
double exposure = GetExposure();
double marketValue = GetMarketValue();
if (0.00 == exposure) return exposure;
return (marketValue - exposure) / exposure;
}
public void Display()
{
for (int slotIndex = 0; slotIndex < Count; slotIndex++)
{
Positions positions = this[slotIndex];
DateTime purchaseDate = (from Position position in positions select position.PurchaseDate).Distinct().FirstOrDefault();
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("******************* B U Y S F O R {0} *****************", Utility.DateTimeToStringMMHDDHYYYY(purchaseDate)));
positions.Display();
}
}
public void AddFromNVPCollection(NVPCollection nvpCollection)
{
SlotPosition slotPosition = SlotPosition.FromNVPCollection(nvpCollection);
if (!ContainsKey(slotPosition.Slot))
{
Add(slotPosition.Slot, new Positions());
}
this[slotPosition.Slot].Add(slotPosition.ToPosition());
}
public List<NVPCollections> ToNVPCollections()
{
List<int> slots = new List<int>(Keys);
List<NVPCollections> nvpCollectionsList = new List<NVPCollections>();
foreach (int slot in slots)
{
Positions positions = this[slot];
SlotPositions slotPositions = new SlotPositions(slot, positions);
NVPCollections nvpCollections = slotPositions.ToNVPCollections();
nvpCollectionsList.Add(nvpCollections);
}
return nvpCollectionsList;
}
}
}

View File

@@ -0,0 +1,781 @@
using MarketData.MarketDataModel;
using MarketData.DataAccess;
using MarketData.Utils;
using System.Linq;
using MarketData.Cache;
using MarketData.Generator.Model;
using MarketData.CNNProcessing;
namespace MarketData.Generator.CMMomentum
{
public class CMBacktestResult
{
public CMBacktestResult()
{
}
public double CashBalance { get; set; }
public bool Success { get; set; }
}
// ********************************************************************************************************************************************************
public class CMMomentumBacktest
{
private double CashBalance { get; set; }
private double NonTradeableCash{get;set;}
public CMParams Parameters { get; set; }
private int HoldingPeriod { get { return Parameters.HoldingPeriod; } }
private int MaxPositions { get { return Parameters.MaxPositions; } }
private List<String> NoTradeSymbols { get { return Utility.ToList(Parameters.NoTradeSymbols); } }
private ActivePositions ActivePositions { 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; }
// ******************************************************************************************************************************************************
//********************************************************** D I S P L A Y G A I N L O S S *********************************************************
// ******************************************************************************************************************************************************
public void DisplayGainLoss(String paramPathSessionFileName)
{
Profiler profiler=new Profiler();
ModelPerformanceSeries performanceSeries=GetModelPerformance(paramPathSessionFileName);
if(null==performanceSeries) return;
MDTrace.WriteLine("Date,Exposure,MarketValue,GainLossDOD,GainLoss,CumulativeGainLoss,R,(1+R),CumProd,CumProd-1,ClosedPositions");
foreach(ModelPerformanceItem modelPerformanceItem in performanceSeries)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\",\"{6}\",\"{7}\",\"{8}\",\"{9}\",\"{10}\"",
modelPerformanceItem.Date.ToShortDateString(),
Utility.FormatCurrency(modelPerformanceItem.Exposure),
Utility.FormatCurrency(modelPerformanceItem.MarketValue),
Utility.FormatCurrency(modelPerformanceItem.GainLossDOD),
Utility.FormatCurrency(modelPerformanceItem.GainLoss),
Utility.FormatCurrency(modelPerformanceItem.CumulativeGainLoss),
Utility.FormatNumber(modelPerformanceItem.R,4),
Utility.FormatNumber(modelPerformanceItem.OnePlusR,4),
Utility.FormatNumber(modelPerformanceItem.CumProd,4),
Utility.FormatNumber(modelPerformanceItem.CumProdMinusOne,4),
modelPerformanceItem.ClosedPositions));
}
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Done, took {0}(ms)",profiler.End()));
}
public static ModelPerformanceSeries GetModelPerformance(String paramPathSessionFileName)
{
try
{
CMSessionParams sessionParams=CMSessionManager.RestoreSession(paramPathSessionFileName);
return GetModelPerformance(sessionParams);
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
return null;
}
}
public static ModelStatistics GetModelStatistics(CMSessionParams sessionParams)
{
ModelStatistics modelStatistics=new ModelStatistics();
try
{
if(null==sessionParams||null==sessionParams.AllPositions||0==sessionParams.AllPositions.Count) return modelStatistics;
double totalTrades=sessionParams.AllPositions.Count;
double winningTrades=sessionParams.AllPositions.Where(x => x.GainLoss>=0.00).Count();
double losingTrades=sessionParams.AllPositions.Where(x => x.GainLoss<0.00).Count();
double averageWinningTrade=sessionParams.AllPositions.Where(x => x.GainLoss>=0.00).Average(x => x.GainLossPcnt)*100.00;
double averageLosingTrade=sessionParams.AllPositions.Where(x => x.GainLoss<0.00).Average(x => x.GainLossPcnt)*100.00;
double percentWinningTrades=(winningTrades/(double)sessionParams.AllPositions.Count)*100.00;
double percentLosingTrades=(losingTrades/(double)sessionParams.AllPositions.Count)*100.00;
double expectation=(percentWinningTrades*averageWinningTrade)/(percentLosingTrades*Math.Abs(averageLosingTrade));
modelStatistics.TotalTrades=(long)totalTrades;
modelStatistics.WinningTrades=(long)winningTrades;
modelStatistics.LosingTrades=(long)losingTrades;
modelStatistics.AverageWinningTradePercentGain=averageWinningTrade;
modelStatistics.AverageLosingTradePercentLoss=averageLosingTrade;
modelStatistics.WinningTradesPercent=percentWinningTrades;
modelStatistics.LosingTradesPercent=percentLosingTrades;
modelStatistics.Expectancy=expectation;
return modelStatistics;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
return modelStatistics;
}
}
public static ModelPerformanceSeries GetModelPerformance(CMSessionParams sessionParams)
{
Profiler profiler=new Profiler();
ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries();
DateGenerator dateGenerator=new DateGenerator();
try
{
if(null==sessionParams)return null;
MarketData.Generator.CMMomentum.Positions combinedPositions=sessionParams.GetCombinedPositions();
DateTime minDate=combinedPositions.Min(x => x.PurchaseDate);
DateTime maxDate=PricingDA.GetLatestDate();
double prevGainLoss=double.NaN;
LocalPriceCache.GetInstance().RemoveDate(maxDate);
List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(minDate,maxDate);
foreach(DateTime currentDate in historicalDates)
{
MarketData.Generator.CMMomentum.Positions openPositions=new MarketData.Generator.CMMomentum.Positions(combinedPositions.Where(x => (x.PurchaseDate<=currentDate&&(!Utility.IsEpoch(x.SellDate)&&x.SellDate>currentDate))||(x.PurchaseDate<=currentDate&&Utility.IsEpoch(x.SellDate))).ToList());
MarketData.Generator.CMMomentum.Positions closedPositions=new MarketData.Generator.CMMomentum.Positions(combinedPositions.Where(x => (!Utility.IsEpoch(x.SellDate)&&x.SellDate.Equals(currentDate))).ToList());
if(0==openPositions.Count&&0==closedPositions.Count) continue;
double gainLoss=0.00;
double gainLossClosedPositions=0.00;
double exposure=0.00;
double marketValue=0.00;
ModelPerformanceItem performanceItem=new ModelPerformanceItem();
foreach(MarketData.Generator.CMMomentum.Position openPosition in openPositions)
{
exposure+=openPosition.Shares*openPosition.PurchasePrice;
if(!LocalPriceCache.GetInstance().ContainsPrice(openPosition.Symbol,currentDate))
{
Prices prices=PricingDA.GetPricesForward(openPosition.Symbol,currentDate,PricingDA.ForwardLookingDays);
LocalPriceCache.GetInstance().Add(prices);
}
Price price=LocalPriceCache.GetInstance().GetPrice(openPosition.Symbol,currentDate);
if(null==price)
{
price=PricingDA.GetPrice(openPosition.Symbol,currentDate);
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString()));
return null;
}
LocalPriceCache.GetInstance().Add(price);
}
gainLoss+=((price.Close*openPosition.Shares)-(openPosition.PurchasePrice*openPosition.Shares));
marketValue+=(price.Close*openPosition.Shares);
}
foreach(MarketData.Generator.CMMomentum.Position closedPosition in closedPositions)
{
double gainLossPosition=(closedPosition.CurrentPrice*closedPosition.Shares)-(closedPosition.PurchasePrice*closedPosition.Shares);
gainLossClosedPositions+=gainLossPosition;
}
performanceItem.Date=currentDate;
performanceItem.Exposure=exposure;
performanceItem.MarketValue=marketValue;
performanceItem.GainLossDOD=double.IsNaN(prevGainLoss)?gainLoss:(gainLoss-prevGainLoss)+gainLossClosedPositions;
performanceItem.GainLoss=gainLoss+gainLossClosedPositions;
performanceItem.ClosedPositions=closedPositions.Count>0?true:false;
performanceSeries.Add(performanceItem);
prevGainLoss=gainLoss;
}
performanceSeries.CalculatePerformance();
return performanceSeries;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
return null;
}
finally
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Done, took {0}(ms)",profiler.End()));
}
}
// ******************************************************************************************************************************************************
//************************************************************** 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;
CMSessionParams 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));
Parameters.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, "************** T O P G A I N E R S *************");
AllPositions.DisplayTopFive();
MDTrace.WriteLine(LogLevel.DEBUG, "************** T O P L O S E R S *************");
AllPositions.DisplayBottomFive();
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));
double gainLoss = 0.00;
if (ActivePositions.Count > 0 && AllPositions.Count > 0)
{
RealtimeGainLoss realtimeGainLoss = GetRealtimeGainLoss(PricingDA.GetLatestDate());
gainLoss = AllPositions.Sum(x => x.GainLoss);
if (null != realtimeGainLoss) gainLoss += realtimeGainLoss.GainLoss;
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Total Gain/Loss {0}", Utility.FormatCurrency(gainLoss)));
}
else if (ActivePositions.Count > 0)
{
RealtimeGainLoss realtimeGainLoss = GetRealtimeGainLoss(PricingDA.GetLatestDate());
if (null != realtimeGainLoss) gainLoss = realtimeGainLoss.GainLoss;
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Total Active Gain/Loss {0}", Utility.FormatCurrency(gainLoss)));
}
else if (AllPositions.Count > 0)
{
gainLoss = AllPositions.Sum(x => x.GainLoss);
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Total Active Gain/Loss {0}", Utility.FormatCurrency(gainLoss)));
}
else MDTrace.WriteLine(LogLevel.DEBUG,"There does not appear to be any trade data in this file.");
}
// ******************************************************************************************************************************************************
// ****************************************************************** C L O S E **********************************************************************
// ******************************************************************************************************************************************************
public bool ClosePosition(String symbol,DateTime purchaseDate,DateTime sellDate,double sellPrice,String pathSessionFile)
{
if(null==pathSessionFile) return false;
CMSessionParams sessionParams=null;
PathSessionFileName=pathSessionFile;
if(null==(sessionParams=RestoreSession()))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Error loading session file {0}",pathSessionFile));
return false;
}
if(!BackupSession()) return false;
Positions activePositions = ActivePositions.GetPositions();
Position position=activePositions.Where(x => x.Symbol.Equals(symbol) && x.PurchaseDate.Equals(purchaseDate)).FirstOrDefault();
if(null==position) // if it is not in the active positions then the position is already closed and we are modifying either the sell date or the sell price
{
position=AllPositions.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;
}
position.SellDate = sellDate;
CashBalance -= position.MarketValue;
position.CurrentPrice = sellPrice;
CashBalance += position.MarketValue;
SaveSession();
return true;
}
position.SellDate = sellDate;
position.CurrentPrice = sellPrice;
CashBalance += position.MarketValue;
ActivePositions.Remove(position);
AllPositions.Add(position);
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Position for symbol '{0}' purchased on {1} is now closed.",symbol,purchaseDate.ToShortDateString()));
SaveSession();
return true;
}
// ******************************************************************************************************************************************************
// *************************************************************************** E D I T ******************************************************************
// ******************************************************************************************************************************************************
public bool EditPosition(String symbol,DateTime purchaseDate,double purchasePrice,String pathSessionFile)
{
if(null==pathSessionFile) return false;
PathSessionFileName=pathSessionFile;
CMSessionParams sessionParams=null;
if(null==(sessionParams=RestoreSession()))
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Error loading session file {0}",pathSessionFile));
return false;
}
if(!BackupSession()) return false;
Positions activePositions = ActivePositions.GetPositions();
Position 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.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)));
CashBalance+=(position.PurchasePrice-purchasePrice)*position.Shares;
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;
}
// ******************************************************************************************************************************************************
// ****************************************************************** B A C K T E S T *****************************************************************
// ******************************************************************************************************************************************************
// Ideally, startDate should be November,February,May,August
// paramStartDate is startDate
// paramAnalysisDate is endDate
public CMBacktestResult PerformBacktest(DateTime paramStartDate, DateTime paramAnalysisDate, String paramPathSessionFileName, CMParams cmParams)
{
CMBacktestResult backTestResult = new CMBacktestResult();
DateGenerator dateGenerator = new DateGenerator();
Parameters = cmParams;
CashBalance = Parameters.InitialCash;
ActivePositions = new ActivePositions();
AllPositions = new Positions();
StartDate = paramStartDate;
TradeDate = paramStartDate;
AnalysisDate = paramAnalysisDate;
PathSessionFileName = paramPathSessionFileName;
CMSessionParams 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));
}
Parameters.DisplayConfiguration();
if(!IsCNNServerAvailable())
{
GBPriceCache.GetInstance().Dispose();
return new CMBacktestResult()
{
Success = false,
CashBalance = CashBalance
};
}
DisplayBalance();
if(TradeDate > AnalysisDate)
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("*** It looks like the TradeDate {0} is greater than the AnalysisDate {1}. Please check the TradeDate in the session file. It should be the last market date of the previous month. You may have altered the system date during the last run.", TradeDate.ToShortDateString(), AnalysisDate.ToShortDateString()));
if(null!=PathSessionFileName)MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Session file: {0}",PathSessionFileName));
}
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(slotIndex,TradeDate,AnalysisDate,CashBalance/((double)HoldingPeriod-(double)ActivePositions.Count),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 (1). Available cash: {0}. Requested Exposure: {1}",Utility.FormatCurrency(CashBalance),Utility.FormatCurrency(positions.Exposure)));
break;
}
ActivePositions.Add(slotIndex, positions);
CashBalance -= positions.Exposure;
DisplayBalance();
}
else
{
Positions slotPositions = ActivePositions[slotIndex];
SellPositions(slotPositions, TradeDate);
DisplaySales(slotPositions, TradeDate);
MDTrace.WriteLine(LogLevel.DEBUG, "********************* S E L L *********************");
slotPositions.Display();
AllPositions.Add(slotPositions);
CashBalance += slotPositions.MarketValue;
ActivePositions[slotIndex].Clear();
DisplayBalance();
double cashAllocation = Math.Min(CashBalance, (ActivePositions.GetExposure() + CashBalance) / HoldingPeriod); // Even out the cash allocation so that no one slot eats up all the cash
Positions positions=BuyPositions(slotIndex,TradeDate,AnalysisDate,cashAllocation,SymbolsHeld());
DisplayPurchases(positions, TradeDate);
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 (2). Available cash: {0}. Requested Exposure: {1}", Utility.FormatCurrency(CashBalance), Utility.FormatCurrency(positions.Exposure)));
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.");
DisplayBalanceFromPositions();
if (null != PathSessionFileName) SaveSession();
for (int slotIndex = 0; slotIndex < HoldingPeriod; slotIndex++)
{
if (!ActivePositions.ContainsKey(slotIndex) || 0 == ActivePositions[slotIndex].Count()) continue;
Positions slotPositions = ActivePositions[slotIndex];
DisplaySales(slotPositions, TradeDate);
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;
GBPriceCache.GetInstance().Dispose();
return backTestResult;
}
// **********************************************************************************************************************************************************
public List<String> SymbolsHeld()
{
return ActivePositions.GetSymbols();
}
// ***************************************************************************************************************************************************
// **************************************************************** S E L L P O S I T I O N S *****************************************************
// ***************************************************************************************************************************************************
// These make the monthly process a bit easier to read
private void DisplaySales(Positions positions,DateTime tradeDate)
{
MDTrace.WriteLine(LogLevel.DEBUG,"********* S E L L S *********");
foreach (Position position in positions)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Sell {0} {1} @ {2} on {3}",position.Symbol,Utility.FormatNumber(position.Shares,3),Utility.FormatCurrency(position.CurrentPrice,2),tradeDate.ToShortDateString()));
}
}
private void DisplayPurchases(Positions positions, DateTime tradeDate)
{
MDTrace.WriteLine(LogLevel.DEBUG,"********* B U Y S *********");
foreach (Position position in positions)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Buy {0} {1} @ {2} on {3}",position.Symbol,Utility.FormatNumber(position.Shares,3),Utility.FormatCurrency(position.PurchasePrice,2),TradeDate.ToShortDateString()));
}
}
private void SellPositions(Positions positions, DateTime sellDate)
{
DateGenerator dateGenerator = new DateGenerator();
foreach (Position position in positions)
{
SellPosition(position, sellDate);
}
}
private void SellPosition(Position position, DateTime sellDate)
{
Price price = 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.SellDate = sellDate;
position.CurrentPrice = price.Close;
}
}
// ***************************************************************************************************************************************************
// **************************************************************** B U Y P O S I T I O N S *****************************************************
// ***************************************************************************************************************************************************
private Positions BuyPositions(int slotIndex,DateTime tradeDate, DateTime analysisDate, double cash, List<String> symbolsHeld)
{
DateGenerator dateGenerator = new DateGenerator();
Positions positions = new Positions();
int positionCount = 0;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("BUY POSITIONS (SlotIndex:{0} TradeDate:{1} AnalysisDate:{2} Cash Allocation:{3})",slotIndex,tradeDate.ToShortDateString(),analysisDate.ToShortDateString(),Utility.FormatCurrency(cash)));
CMGeneratorResult cmGeneratorResult = CMMomentumGenerator.GenerateCMCandidates(tradeDate, analysisDate, Parameters, symbolsHeld);
if (!cmGeneratorResult.Success)
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("GenerateCMCandidates failed with message {0}",cmGeneratorResult.LastMessage));
return positions;
}
for (int index = 0; index < cmGeneratorResult.CMCandidates.Count;index++)
{
CMCandidate cmCandidate = cmGeneratorResult.CMCandidates[index];
Price price = GetPrice(cmCandidate.Symbol, tradeDate);
if (null == price)
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Cannot locate a price for {0} on {1}", cmCandidate.Symbol, Utility.DateTimeToStringMMHDDHYYYY(tradeDate)));
continue;
}
Position position = new Position();
position.Symbol = cmCandidate.Symbol;
position.Beta = cmCandidate.Beta;
position.BetaMonths = cmCandidate.BetaMonths;
position.SharpeRatio = cmCandidate.SharpeRatio;
position.PurchaseDate = tradeDate;
position.PurchasePrice = price.Close;
position.CurrentPrice = double.NaN;
position.Score = cmCandidate.Score;
position.CNNPrediction = cmCandidate.CNNPrediction;
positions.Add(position);
positionCount++;
if (positionCount >= MaxPositions) break;
}
positions=PerformPositionSizing(positions,cash,tradeDate,cmGeneratorResult.InFallback);
if(Parameters.UseMaxPositionBucketWeight)
{
positions=AdjustPositionWeights(positions);
}
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("BUY POSITIONS (SlotIndex:{0} TradeDate:{1} AnalysisDate:{2} Total Exposure:{3})", slotIndex,tradeDate.ToShortDateString(), analysisDate.ToShortDateString(), Utility.FormatCurrency(positions.Sum(x=>x.Exposure))));
return positions;
}
// ***************************************************************************************************************************************************
// ************************************************************************ G E T P R I C E ******************************************************
// ***************************************************************************************************************************************************
private Price GetPrice(String symbol, DateTime priceDate)
{
DateGenerator dateGenerator = new DateGenerator();
priceDate = dateGenerator.GetPrevBusinessDay(priceDate);
Price price = GBPriceCache.GetInstance().GetPrice(symbol, priceDate);
if (null == price) price = GBPriceCache.GetInstance().GetPrice(symbol, dateGenerator.FindPrevBusinessDay(priceDate));
return price;
}
// ***************************************************************************************************************************************************
// ***************************************************************** P O S I T I O N S I Z I N G **************************************************
// ***************************************************************************************************************************************************
private Positions PerformPositionSizing(Positions positions, double cash, DateTime tradeDate,bool inFallback=false)
{
if (null == positions || 0 == positions.Count) return positions;
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("PERFORM POSITION SIZING Positions:{0} Cash:{1}", positions.Count, Utility.FormatCurrency(cash)));
if (inFallback) positions = new Positions(positions.Take(1).ToList()); // if processing fallbacks then make sure we've just got a single holding
if (inFallback && !double.IsNaN(Parameters.FallbackMaxAlloc))
{
cash = cash < Parameters.FallbackMaxAlloc ? cash : Parameters.FallbackMaxAlloc;
Position position = positions[0];
position.Weight = 1.00;
position.TargetBetaOverBeta = Parameters.TargetBeta / position.Beta;
position.RiskAdjustedWeight = 1.00;
position.RiskAdjustedAllocation = cash * position.RiskAdjustedWeight;
if (position.RiskAdjustedAllocation < position.PurchasePrice) position.RiskAdjustedAllocation = position.PurchasePrice;
position.Shares = Math.Floor(position.RiskAdjustedAllocation / position.PurchasePrice);
return positions;
}
double totalTargetBetaOverBeta = 0.00;
double maxPositions = positions.Count;
foreach (Position position in positions)
{
position.Weight = 1 / maxPositions;
position.TargetBetaOverBeta = Parameters.TargetBeta / position.Beta;
}
totalTargetBetaOverBeta = (from Position position in positions select position.TargetBetaOverBeta).Sum();
foreach (Position position in positions)
{
position.RiskAdjustedWeight = position.TargetBetaOverBeta/totalTargetBetaOverBeta;
position.RiskAdjustedAllocation = cash * position.RiskAdjustedWeight;
if (position.RiskAdjustedAllocation < position.PurchasePrice) position.RiskAdjustedAllocation = position.PurchasePrice;
position.Shares = Math.Floor(position.RiskAdjustedAllocation / position.PurchasePrice);
}
if (positions.Exposure > cash) positions = new Positions(positions.Where(x => x.Shares > 1).ToList());
return positions;
}
// ***************************************************************************************************************************************************
// ***************************************************************** P O S I T I O N W E I G H T S ************************************************
// ***************************************************************************************************************************************************
// This is a post-stage to the above PositionSizing. It works by examing the weights of the positions in the bucket and ensuring that no position weight is larger than UseMaxPositionBucketWeightMaxWeight.
// If an overweight position is located then it's exposure is reduced to UseMaxPositionBucketWeightMaxWeight and the remaining positions divide the excess exposure evenly.
// This prevents any single position from eclipsing the other positions in the bucket.
private Positions AdjustPositionWeights(Positions positions)
{
if(null==positions || positions.Count<=1 || false==Parameters.UseMaxPositionBucketWeight) return positions;
double totalExposure=positions.Exposure;
Position overweightPosition=positions.Where(x=>x.Exposure/totalExposure>Parameters.UseMaxPositionBucketWeightMaxWeight).FirstOrDefault();
if(null==overweightPosition)return positions;
double weightToRedistribute=(overweightPosition.Exposure/totalExposure)-Parameters.UseMaxPositionBucketWeightMaxWeight;
foreach(Position position in positions)
{
double newWeight=double.NaN;
if(position==overweightPosition)
{
newWeight=(position.Exposure/totalExposure)-weightToRedistribute;
}
else
{
newWeight=(position.Exposure/totalExposure)+(weightToRedistribute/(double) (positions.Count-1));
}
position.Shares=Math.Floor((newWeight*totalExposure)/position.PurchasePrice);
}
return positions;
}
// **********************************************************************************************************************************************************
// **************************************************************** 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 = PricingDA.GetPrice(position.Symbol, tradeDate);
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;
}
// *********************************************************************************************************************************************************************
// *********************************************************************************************************************************************************************
// *********************************************************************************************************************************************************************
private void DisplayBalance()
{
MDTrace.WriteLine(LogLevel.DEBUG, "EXPOSURE,AVAILABLE CASH,TOTAL ACCOUNT");
if (!double.IsNaN(ActivePositions.GetMarketValue()))
{
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))));
}
else
{
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.GetExposure() + CashBalance))));
}
}
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;
}
// ****************************************************************************************************************************************
// ************************************************************** C N N S E R V E R ***************************************************
// ****************************************************************************************************************************************
private bool IsCNNServerAvailable()
{
if(!Parameters.UseCNN)return true;
CNNClient cnnClient=new CNNClient(Parameters.UseCNNHost);
if(!cnnClient.Ping())
{
String strMessage=String.Format("******* UseCNN=true but the server is not responding. {0} *******",Parameters.UseCNNHost);
MDTrace.WriteLine(LogLevel.DEBUG,strMessage);
return false;
}
return true;
}
// ****************************************************************************************************************************************
// **************************************************************** S E S S I O N M A N A G E M E N T ***********************************
// ****************************************************************************************************************************************
/// <summary>
/// Restore the session file from disk.
/// Take note that we do not restore AnalysisDate from the session file. The analysis date must always reflect the current analysis date.
/// </summary>
/// <returns></returns>
public CMSessionParams RestoreSession()
{
try
{
CMSessionManager sessionManager = new CMSessionManager();
if (!CMSessionManager.SessionAvailable(PathSessionFileName)) return null;
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Restoring session from '{0}'", PathSessionFileName));
CMSessionParams sessionParams = CMSessionManager.RestoreSession(PathSessionFileName);
TradeDate = sessionParams.TradeDate;
if (TradeDate.Date < AnalysisDate.Date) TradeDate = AnalysisDate;
StartDate = sessionParams.StartDate;
Parameters = sessionParams.CMParams;
ActivePositions = sessionParams.ActivePositions;
AllPositions = sessionParams.AllPositions;
Cycle = sessionParams.Cycle;
CashBalance = sessionParams.CashBalance;
NonTradeableCash = sessionParams.NonTradeableCash;
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));
CMSessionParams sessionParams = new CMSessionParams();
CMSessionManager sessionManager = new CMSessionManager();
sessionParams.LastUpdated = Today();
sessionParams.TradeDate = TradeDate;
sessionParams.StartDate = StartDate;
sessionParams.AnalysisDate = AnalysisDate;
sessionParams.CMParams = Parameters;
sessionParams.ActivePositions = ActivePositions;
sessionParams.AllPositions = AllPositions;
sessionParams.Cycle = Cycle;
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));
CMSessionParams sessionParams = new CMSessionParams();
CMSessionManager sessionManager = new CMSessionManager();
sessionParams.LastUpdated = Today();
sessionParams.TradeDate = TradeDate;
sessionParams.StartDate = StartDate;
sessionParams.AnalysisDate = AnalysisDate;
sessionParams.CMParams = Parameters;
sessionParams.ActivePositions = ActivePositions;
sessionParams.AllPositions = AllPositions;
sessionParams.Cycle = Cycle;
sessionParams.CashBalance = CashBalance;
sessionParams.NonTradeableCash = NonTradeableCash;
return sessionManager.SaveSession(sessionParams, backupFileName);
}
}
}

View File

@@ -0,0 +1,37 @@
namespace MarketData.Generator.CMMomentum
{
public class CMCandidates : List<CMCandidate>
{
public CMCandidates()
{
}
public CMCandidates(List<CMCandidate> cmCandidates)
{
foreach (CMCandidate cmCandidate in cmCandidates) Add(cmCandidate);
}
}
public class CMCandidate
{
public CMCandidate()
{
Violation = false;
}
public String Symbol { get; set; }
public DateTime AnalysisDate { get; set; }
public DateTime TradeDate { get; set; }
public int DayCount { get; set; }
public double Slope { get; set; }
public double AnnualizedReturn { get; set; }
public double Score { get; set; }
public double RSquared { get; set; }
public double Beta { get; set; }
public int BetaMonths { get; set; }
public double SharpeRatio { get; set; }
public long Volume { get; set; }
public bool Violation { get; set; }
public String Reason { get; set; }
public String ReasonCategory { get; set; }
public bool CNNPrediction{get;set;}
}
}

View File

@@ -0,0 +1,340 @@
using MarketData.DataAccess;
using MarketData.Generator.Indicators;
using MarketData.Generator.MovingAverage;
using MarketData.MarketDataModel;
using MarketData.Numerical;
using MarketData.Utils;
namespace MarketData.Generator.CMMomentum
{
public class CMCandidateGenerator
{
private CMCandidateGenerator()
{
}
// *******************************************************************************************************************************************************************************
// ************************************************************************* G E N E R A T E C A N D I D A T E *****************************************************************
// *******************************************************************************************************************************************************************************
public static CMCandidate GenerateCandidate(String symbol, DateTime tradeDate, DateTime analysisDate, CMParams cmParams, List<String> symbolsHeld = null)
{
CMCandidate cmCandidate = new CMCandidate();
// No trade symbols
if (null!=cmParams.NoTradeSymbolsList&&cmParams.NoTradeSymbolsList.Any(x => x.Equals(symbol)))
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("Candidate in the No-Trade list.");
cmCandidate.ReasonCategory = String.Format("Candidate in the No-Trade list.");
return cmCandidate;
}
// Check if the symbol is held in any open positions
if (null != symbolsHeld && symbolsHeld.Any(x => x.Equals(symbol, StringComparison.CurrentCultureIgnoreCase)))
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("Already held.");
cmCandidate.ReasonCategory = String.Format("Already held.");
return cmCandidate;
}
Fundamental fundamental = FundamentalDA.GetFundamentalMaxDate(symbol, tradeDate);
if (null == fundamental)
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("No fundamental.");
cmCandidate.ReasonCategory = String.Format("No fundamental.");
return cmCandidate;
}
if (!(fundamental.MarketCap >= cmParams.MarketCapLowerLimit))
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("MarketCapLowerLimit constraint violation.");
cmCandidate.ReasonCategory = String.Format("MarketCapLowerLimit constraint violation.");
return cmCandidate;
}
// Equity check
CompanyProfile companyProfile = CompanyProfileDA.GetCompanyProfile(symbol);
if (!companyProfile.IsEquity)
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("Candidate is not an equity.");
cmCandidate.ReasonCategory = String.Format("Candidate is not an equity.");
return cmCandidate;
}
// Moving Average check
Prices pricesDMA = PricingDA.GetPrices(symbol, tradeDate, cmParams.MovingAverageConstraintDays + 15);
if (null == pricesDMA || (cmParams.MovingAverageConstraintDays + 15) != pricesDMA.Count)
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("Insufficient pricing to generate moving average.");
cmCandidate.ReasonCategory = String.Format("Insufficient pricing to generate moving average.");
return cmCandidate;
}
DMAPrices dmaPrices = MovingAverageGenerator.GenerateMovingAverage(pricesDMA, cmParams.MovingAverageConstraintDays);
if (dmaPrices[0].CurrentPrice < dmaPrices[0].AVGPrice)
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("Current price is less than moving average.");
cmCandidate.ReasonCategory = String.Format("Current price is less than moving average.");
return cmCandidate;
}
Prices prices = PricingDA.GetPrices(symbol, tradeDate, cmParams.DayCount); // The prices come back with the most recent date in the lowest index. We want the earliest date in the lowest index
if (null == prices || cmParams.DayCount != prices.Count)
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("Insufficient pricing data.");
cmCandidate.ReasonCategory = String.Format("Insufficient pricing data.");
return cmCandidate;
}
// Filter penny stocks - don't trade anything less than $1.00
if (prices[0].Close < 1.00 || prices[0].Open < 1.00)
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("Penny stock violation {0} Close price is {1}, Open price is ", symbol, Utility.FormatCurrency(prices[0].Close), Utility.FormatCurrency(prices[0].Open));
cmCandidate.ReasonCategory = String.Format("Penny stock violation.");
return cmCandidate;
}
// Capture latest Volume - we'll do a min check later on
cmCandidate.Volume = prices[0].Volume;
// Liquidity check
if (((from Price price in prices where price.Volume < 10000 select price).Count()) > 1)
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("Liquidity check violation for {0}. Price volume must be above 10,000 ", symbol);
cmCandidate.ReasonCategory = String.Format("Liquidity check violation.");
return cmCandidate;
}
// OverExtended check
if(cmParams.UseOverExtendedIndicator)
{
bool? result=OverExtendedIndicator.IsOverextended(symbol,tradeDate,cmParams.UseOverExtendedIndicatorDays,cmParams.UseOverExtendedIndicatorViolationThreshhold,cmParams.UseOverExtendedIndicatorMarginPercent);
if(null!=result && true==result.Value)
{
cmCandidate.Violation=true;
cmCandidate.Reason=String.Format("OverExtended violation for {0}. The security is price overextended for TradeDate:{1}.",symbol,Utility.DateTimeToStringMMHDDHYYYY(tradeDate));
cmCandidate.ReasonCategory=String.Format("OverExtended violation. The security is price overextended.");
return cmCandidate;
}
}
// Momentum Check
if (HasReturnViolation(prices,cmParams.DailyReturnLimit)) // Check the return stream. If any daily return exceeds DailyReturnLimit then we discard.
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("Daily return violation for {0}. A daily return exceeded {1}%.", symbol, cmParams.DailyReturnLimit);
cmCandidate.ReasonCategory = String.Format("Daily return violation. A daily return exceeded expectations.");
return cmCandidate;
}
prices.Reverse(); // Reverse the series here.
double[] logPrices = null;
logPrices = new double[prices.Count];
for (int index = 0; index < prices.Count; index++)
{
Price price = prices[index];
logPrices[index] = Math.Log(price.Close);
}
LeastSquaresResultWithR2 leastSquaresResult = LeastSquaresHelper.CalculateLeastSquaresWithR2(logPrices);
cmCandidate = new CMCandidate();
cmCandidate.Symbol = symbol;
cmCandidate.AnalysisDate = analysisDate;
cmCandidate.TradeDate = tradeDate;
cmCandidate.DayCount = cmParams.DayCount;
cmCandidate.Slope = leastSquaresResult.Slope;
cmCandidate.AnnualizedReturn = Math.Pow(Math.Exp(cmCandidate.Slope), 252); //cmCandidate.AnnualizedReturn=Math.Pow(1.00+cmCandidate.Slope,252);
if (cmCandidate.Slope < 0) cmCandidate.AnnualizedReturn *= -1.00; // preserve the sign of the slope
cmCandidate.Score = leastSquaresResult.RSquared * cmCandidate.AnnualizedReturn; // The greater the score the higher the rank
cmCandidate.RSquared = leastSquaresResult.RSquared;
cmCandidate.Beta = BetaGenerator.Beta(symbol, tradeDate, cmParams.BetaMonths);
cmCandidate.BetaMonths = cmParams.BetaMonths;
if (double.IsNaN(cmCandidate.Beta))
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("Unable to calculate {0} month beta for {1} ", cmParams.BetaMonths, symbol);
cmCandidate.ReasonCategory = String.Format("Beta cannot be calculated.");
return cmCandidate;
}
if (cmCandidate.Beta <= 0.00)
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("Beta for {0} is less than or equal to zero {1}", symbol, cmCandidate.Beta);
cmCandidate.ReasonCategory = String.Format("Beta is less than or equal to zero.");
return cmCandidate;
}
if (cmParams.UseMaxBeta && cmCandidate.Beta > cmParams.MaxBeta)
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("Beta for {0} exceeds maximum allowed. Candidate beta {1}, Max Beta:{2}", symbol, cmCandidate.Beta,cmParams.MaxBeta);
cmCandidate.ReasonCategory = String.Format("Beta exceeds maximum allowed.");
return cmCandidate;
}
cmCandidate.SharpeRatio = SharpeRatioGenerator.GenerateSharpeRatio(cmCandidate.Symbol, tradeDate);
if (double.IsNaN(cmCandidate.SharpeRatio))
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("Unable to calculate Sharpe Ratio for {0}", symbol);
cmCandidate.ReasonCategory = String.Format("Unable to calculate Sharpe Ratio.");
return cmCandidate;
}
return cmCandidate;
}
// *******************************************************************************************************************************************************************************
// ************************************************************* G E N E R A T E C A N D I D A T E F O R F A L L B A C K ***************************************************
// *******************************************************************************************************************************************************************************
public static CMCandidate GenerateCandidateForFallback(String symbol, DateTime tradeDate, DateTime analysisDate, CMParams cmParams, List<String> symbolsHeld = null)
{
CMCandidate cmCandidate = new CMCandidate();
// Check MarketCap
Fundamental fundamental = FundamentalDA.GetFundamentalMaxDate(symbol, tradeDate);
if (null == fundamental)
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("No fundamental for {0}.", symbol);
cmCandidate.ReasonCategory = String.Format("No fundamental.");
return cmCandidate;
}
if (!(fundamental.MarketCap >= cmParams.MarketCapLowerLimit))
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("MarketCapLowerLimit constraint violation for {0}.", symbol);
cmCandidate.ReasonCategory = String.Format("MarketCapLowerLimit constraint violation.");
return cmCandidate;
}
// Check if the symbol is held in any open positions
if (null != symbolsHeld && symbolsHeld.Any(x => x.Equals(symbol)))
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("{0} is already held.", symbol);
cmCandidate.ReasonCategory = String.Format("Candidate already held.");
return cmCandidate;
}
Prices prices = PricingDA.GetPrices(symbol, tradeDate, cmParams.DayCount); // The prices come back with the most recent date in the lowest index. We want the earliest date in the lowest index
if (null == prices || cmParams.DayCount != prices.Count)
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("Insufficient pricing data for {0}. Required {1} days.", symbol, cmParams.DayCount);
cmCandidate.ReasonCategory = String.Format("Insufficient pricing data.");
return cmCandidate;
}
cmCandidate.Volume = prices[0].Volume;
// Momentum Check
if (HasReturnViolation(prices, cmParams.DailyReturnLimit))
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("Daily return violation for {0}. A daily return exceeded {1}%.", symbol, cmParams.DailyReturnLimit);
cmCandidate.ReasonCategory = String.Format("Daily return violation. A daily return exceeded the limit.");
return cmCandidate;
}
prices.Reverse(); // Reverse the series here.
double[] logPrices = null;
logPrices = new double[prices.Count];
for (int index = 0; index < prices.Count; index++)
{
Price price = prices[index];
logPrices[index] = Math.Log(price.Close);
}
LeastSquaresResultWithR2 leastSquaresResult = LeastSquaresHelper.CalculateLeastSquaresWithR2(logPrices);
cmCandidate = new CMCandidate();
cmCandidate.Symbol = symbol;
cmCandidate.AnalysisDate = analysisDate;
cmCandidate.TradeDate = tradeDate;
cmCandidate.DayCount = cmParams.DayCount;
cmCandidate.Slope = leastSquaresResult.Slope;
cmCandidate.AnnualizedReturn = Math.Pow(Math.Exp(cmCandidate.Slope), 252); //cmCandidate.AnnualizedReturn=Math.Pow(1.00+cmCandidate.Slope,252);
if (cmCandidate.Slope < 0) cmCandidate.AnnualizedReturn *= -1.00; // preserve the sign of the slope
cmCandidate.Score = leastSquaresResult.RSquared * cmCandidate.AnnualizedReturn; // The greater the score the higher the rank
cmCandidate.RSquared = leastSquaresResult.RSquared;
cmCandidate.Beta = BetaGenerator.Beta(symbol,cmParams.BetaMonths); // generate a 90 day Beta
cmCandidate.BetaMonths = cmParams.BetaMonths;
if (double.IsNaN(cmCandidate.Beta))
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("Unable to calculate {0} month beta for {1} ", cmParams.BetaMonths, symbol);
cmCandidate.ReasonCategory = String.Format("Beta could not be calculated.");
return cmCandidate;
}
if (cmCandidate.Beta <= 0.00)
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("Beta for {0} is less than or equal to zero {1}", symbol, cmCandidate.Beta);
cmCandidate.ReasonCategory = String.Format("Beta is less than or equal to zero.");
return cmCandidate;
}
if (cmParams.UseMaxBeta && cmCandidate.Beta > cmParams.MaxBeta)
{
cmCandidate.Violation = true;
cmCandidate.Reason = String.Format("Beta for {0} exceeds maximum allowed. Candidate beta {1}, Max Beta:{2}", symbol, cmCandidate.Beta, cmParams.MaxBeta);
cmCandidate.ReasonCategory = String.Format("Beta exceeds maximum allowed.");
return cmCandidate;
}
cmCandidate.SharpeRatio = SharpeRatioGenerator.GenerateSharpeRatio(cmCandidate.Symbol, tradeDate);
if (double.IsNaN(cmCandidate.SharpeRatio)) cmCandidate.SharpeRatio = 0.00; // don't care about this for the fallback candidate
return cmCandidate;
}
// Given the list of candidates select the one candidate with the most favorable slope as an absolute fallback
// *******************************************************************************************************************************************************************************
// ************************************************************ G E N E R A T E C A N D I D A T E O F L A S T R E S O R T ***************************************************
// *******************************************************************************************************************************************************************************
public static CMCandidate GetFallbackCandidateOfLastResort(List<String> candidates, DateTime tradeDate, DateTime analysisDate, CMParams cmParams)
{
CMCandidates cmCandidates = new CMCandidates();
foreach (String candidate in candidates)
{
Prices prices = PricingDA.GetPrices(candidate, tradeDate, cmParams.DayCount); // The prices come back with the most recent date in the lowest index. We want the earliest date in the lowest index
if (null == prices || cmParams.DayCount != prices.Count) continue;
prices.Reverse(); // Reverse the series here.
double[] logPrices = null;
logPrices = new double[prices.Count];
for (int index = 0; index < prices.Count; index++)
{
Price price = prices[index];
logPrices[index] = Math.Log(price.Close);
}
LeastSquaresResultWithR2 leastSquaresResult = LeastSquaresHelper.CalculateLeastSquaresWithR2(logPrices);
CMCandidate cmCandidate = new CMCandidate();
cmCandidate = new CMCandidate();
cmCandidate.Symbol = candidate;
cmCandidate.AnalysisDate = analysisDate;
cmCandidate.TradeDate = tradeDate;
cmCandidate.DayCount = cmParams.DayCount;
cmCandidate.Slope = leastSquaresResult.Slope;
cmCandidate.AnnualizedReturn = Math.Pow(Math.Exp(cmCandidate.Slope), 252); //cmCandidate.AnnualizedReturn=Math.Pow(1.00+cmCandidate.Slope,252);
if (cmCandidate.Slope < 0) cmCandidate.AnnualizedReturn *= -1.00; // preserve the sign of the slope
cmCandidate.Score = leastSquaresResult.RSquared * cmCandidate.AnnualizedReturn; // The greater the score the higher the rank
cmCandidate.RSquared = leastSquaresResult.RSquared;
cmCandidate.Beta = BetaGenerator.Beta(candidate,tradeDate,cmParams.BetaMonths);
cmCandidate.BetaMonths = cmParams.BetaMonths;
if (cmCandidate.Beta <= 0) cmCandidate.Beta = .01;
cmCandidate.SharpeRatio = SharpeRatioGenerator.GenerateSharpeRatio(cmCandidate.Symbol, tradeDate);
if (double.IsNaN(cmCandidate.SharpeRatio)) cmCandidate.SharpeRatio = 0.00; // don't care about this for the fallback candidate
cmCandidates.Add(cmCandidate);
}
return (from CMCandidate cmCandidate in cmCandidates select cmCandidate).OrderByDescending(x => x.Slope).ToList().Take(1).FirstOrDefault();
}
//private static bool HasReturnViolation(float[] dailyReturns,double dailyReturnLimit)
//{
// foreach (float dailyReturn in dailyReturns) if (dailyReturn > dailyReturnLimit) return true;
// return false;
//}
private static bool HasReturnViolation(Prices prices, double dailyReturnLimit)
{
float[] dailyReturns = prices.GetReturns();
int index=0;
String symbol = prices.Take(1).First().Symbol;
for(;index<dailyReturns.Length;index++)
{
if (Math.Abs(dailyReturns[index]) > dailyReturnLimit)
{
return true;
}
}
return false;
}
}
}

View File

@@ -0,0 +1,20 @@
namespace MarketData.Generator.CMMomentum
{
public class CMGeneratorResult
{
public CMGeneratorResult()
{
Success = false;
InFallback = false;
CMCandidates = new CMCandidates();
CMCandidatesWithViolation = new CMCandidates();
Messages = new List<String>();
}
public CMCandidates CMCandidates { get; set; }
public CMCandidates CMCandidatesWithViolation { get; set; }
public bool Success { get; set; }
public bool InFallback { get; set; }
public List<String> Messages { get; set; }
public String LastMessage { get { if (null == Messages || 0 == Messages.Count) return ""; else return Messages[0]; } }
}
}

View File

@@ -0,0 +1,161 @@
using MarketData.CNNProcessing;
using MarketData.DataAccess;
using MarketData.Generator.MovingAverage;
using MarketData.MarketDataModel;
using MarketData.Utils;
namespace MarketData.Generator.CMMomentum
{
public class CMMomentumGenerator
{
private CMMomentumGenerator()
{
}
public static CMGeneratorResult GenerateCMCandidates(DateTime tradeDate,DateTime analysisDate,CMParams cmParams,List<String> symbolsHeld)
{
CMGeneratorResult cmGeneratorResult = new CMGeneratorResult();
try
{
if(cmParams.UseCNN)
{
CNNClient cnnClient=new CNNClient(cmParams.UseCNNHost);
if(!cnnClient.Ping())
{
cmGeneratorResult.Success=false;
cmGeneratorResult.Messages.Add(String.Format("The CNN server at {0} is not responding",cmParams.UseCNNHost));
return cmGeneratorResult;
}
}
List<String> symbols = PricingDA.GetSymbols();
bool checkBenchmarkSMAResult = CheckBenchmarkSMA(tradeDate,cmParams, cmGeneratorResult);
if (!checkBenchmarkSMAResult && false == cmGeneratorResult.Success) return cmGeneratorResult; // indicates an error calulating the moving average
if (false == checkBenchmarkSMAResult)
{
MDTrace.WriteLine(String.Format("The benchmark simple moving average is less than the benchmark price. Benchamrk:{0}, Days:{1}",cmParams.Benchmark,cmParams.BenchmarkMovingAverageDays));
cmGeneratorResult.CMCandidates = GenerateFallbackCandidates(tradeDate,analysisDate,cmParams,symbolsHeld);
cmGeneratorResult.InFallback = true;
return cmGeneratorResult;
}
for(int index=0;index<symbols.Count;index++)
{
String symbol = symbols[index];
if (0 == (index % 500)) MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Processing item {0} of {1}", index + 1, symbols.Count));
CMCandidate cmCandidate = CMCandidateGenerator.GenerateCandidate(symbol,tradeDate,analysisDate,cmParams,symbolsHeld);
if (null == cmCandidate) continue;
if (cmCandidate.Violation)
{
cmGeneratorResult.CMCandidatesWithViolation.Add(cmCandidate);
}
else
{
if(cmParams.UseCNN)PredictCandidate(cmCandidate,cmParams);
cmGeneratorResult.CMCandidates.Add(cmCandidate);
}
}
if(null!=cmGeneratorResult.CMCandidatesWithViolation && 0!=cmGeneratorResult.CMCandidatesWithViolation.Count)
{
MDTrace.WriteLine(LogLevel.DEBUG,"**************** C A N D I D A T E S U M M A R Y ************************");
IEnumerable<Tuple<string, int>> groups = cmGeneratorResult.CMCandidatesWithViolation.GroupBy(x => x.ReasonCategory).OrderByDescending(group => group.Count()).Select(group => Tuple.Create(group.Key, group.Count()));
foreach(Tuple<string, int> group in groups)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Group: {0} Count:{1}",group.Item1, group.Item2));
}
MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Total Considered : {cmGeneratorResult.CMCandidates.Count+cmGeneratorResult.CMCandidatesWithViolation.Count}"));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Total Disqualified : {cmGeneratorResult.CMCandidatesWithViolation.Count}"));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Total Eligible : {cmGeneratorResult.CMCandidates.Count}"));
MDTrace.WriteLine(LogLevel.DEBUG,"******************************************************************************************************");
}
if (0 == cmGeneratorResult.CMCandidates.Count)
{
cmGeneratorResult.CMCandidates = GenerateFallbackCandidates(tradeDate, analysisDate, cmParams,symbolsHeld);
cmGeneratorResult.InFallback = true;
}
if (null == cmGeneratorResult.CMCandidates || 0 == cmGeneratorResult.CMCandidates.Count)
{
MDTrace.WriteLine(String.Format("Unable to produce any candidates for TradeDate {0}", tradeDate));
return null;
}
cmGeneratorResult.CMCandidates = new CMCandidates((from CMCandidate cmCandidate in cmGeneratorResult.CMCandidates select cmCandidate).OrderByDescending(x => x.Score).ThenByDescending(x=>x.SharpeRatio).ToList().Take(cmParams.MaxPositions).ToList());
cmGeneratorResult.Success = true;
return cmGeneratorResult;
}
catch (Exception exception)
{
cmGeneratorResult.Success = false;
cmGeneratorResult.Messages.Add(exception.ToString());
MDTrace.WriteLine(LogLevel.DEBUG, exception.ToString());
return cmGeneratorResult;
}
}
public static CMCandidates GenerateFallbackCandidates(DateTime tradeDate,DateTime analysisDate,CMParams cmParams,List<String> symbolsHeld)
{
CMCandidates cmCandidates = new CMCandidates();
if (null == cmParams || null == cmParams.FallbackCandidateBestOf) return null;
List<String> fallbackCandidateSymbols = Utility.ToList(cmParams.FallbackCandidateBestOf);
if (null != symbolsHeld && 0 != symbolsHeld.Count) fallbackCandidateSymbols = fallbackCandidateSymbols.Except(symbolsHeld).ToList();
foreach (String symbol in fallbackCandidateSymbols)
{
CMCandidate cmCandidate = CMCandidateGenerator.GenerateCandidateForFallback(symbol, tradeDate,analysisDate,cmParams,null);
if (null == cmCandidate || cmCandidate.Violation) continue;
cmCandidates.Add(cmCandidate);
}
cmCandidates = new CMCandidates((from CMCandidate cmCandidate in cmCandidates select cmCandidate).OrderByDescending(x => x.Score).ToList());
cmCandidates = new CMCandidates(cmCandidates.Take(1).ToList());
if (null == cmCandidates || 0 == cmCandidates.Count) // Guarantee that we get a candidate
{
CMCandidate cmCandidate = CMCandidateGenerator.GetFallbackCandidateOfLastResort(Utility.ToList(cmParams.FallbackCandidateBestOf), tradeDate, analysisDate, cmParams);
cmCandidates.Add(cmCandidate);
}
return cmCandidates;
}
private static bool CheckBenchmarkSMA(DateTime tradeDate,CMParams cmParams, CMGeneratorResult cmGeneratorResult)
{
Prices pricesDMA = PricingDA.GetPrices(cmParams.Benchmark, tradeDate, cmParams.BenchmarkMovingAverageDays + 15);
if (null == pricesDMA || (cmParams.BenchmarkMovingAverageDays + 15) != pricesDMA.Count)
{
cmGeneratorResult.Success = false;
cmGeneratorResult.Messages.Add(String.Format("Insufficient pricing to generate {0} day moving average for {0}. Required {2} days.", cmParams.Benchmark, cmParams.BenchmarkMovingAverageDays, cmParams.BenchmarkMovingAverageDays + 15));
return false;
}
DMAPrices dmaPrices = MovingAverageGenerator.GenerateMovingAverage(pricesDMA, cmParams.BenchmarkMovingAverageDays);
if (dmaPrices[0].CurrentPrice < dmaPrices[0].AVGPrice)
{
cmGeneratorResult.Success = true;
cmGeneratorResult.Messages.Add(String.Format("Current price for {0} is less than moving average. {1}<{2} on {3}", cmParams.Benchmark, dmaPrices[0].CurrentPrice, dmaPrices[0].AVGPrice, dmaPrices[0].Date.ToShortDateString()));
return false;
}
return true;
}
// This method is made public in order that it can be tested
public static bool PredictCandidate(CMCandidate cmCandidate,CMParams cmParams)
{
try
{
CNNClient cnnClient=new CNNClient(cmParams.UseCNNHost);
DataProcessor dataProcessor=new DataProcessor();
dataProcessor.Width=128;
dataProcessor.Height=128;
dataProcessor.PenWidth=1;
TestCase testCase=new TestCase(cmCandidate.Symbol,cmCandidate.TradeDate,cmParams.UseCNNDayCount,TestCase.CaseType.Test,TestCase.GenerateType.BollingerBand,TestCase.OutputType.OutputStream);
dataProcessor.ProcessData(testCase);
String prediction = cnnClient.Predict(CNNClient.Model.resnet50_20241024_270,testCase.LastStream);
prediction=prediction.Substring(prediction.IndexOf("-->"));
int result=int.Parse(Utility.BetweenString(prediction,"[[","]"));
if(1==result)
{
cmCandidate.Score*=(1.00+cmParams.UseCNNRewardPercentDecimal); // increase the score by the percentage indicated in the params settings
cmCandidate.CNNPrediction=true;
}
return true;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Error encountered calling convolutional model at {0}. Exception was {1}",cmParams.UseCNNHost,exception.ToString()));
return false;
}
}
}
}

View File

@@ -0,0 +1,193 @@
using MarketData.Utils;
namespace MarketData.Generator.CMMomentum
{
public class CMParams
{
public CMParams()
{
DayCount = 90; // The lookback period
AnalysisDate = DateTime.Now.Date; // The analysis date of the run
TradeDate = DateTime.Now; // The current trade date
DailyReturnLimit = .25; // .15 was producing a lot unqualified candidates, this test to .25. Reject candidates that exceed DailyReturnLimit within the lookback period
MovingAverageConstraintDays = 100; // If current price of a candidate is below the DMA(MovingAverageConstraintDays) then it is rejected
FallbackCandidateBestOf = "SHV,NEAR,BIL,GSY,AGG,ACWX,GSY,SCHF,IXUS,DBEF,IEFA"; // if set then the fallback candidate is selected as the best 252 day return in this comma seperated list (i.e.) "SHV,ACWX,AGG"
FallbackMaxAlloc = 1000; // Max Allocation for Fallback candidate.
Benchmark = "SPY"; // This is the benchmark security
BenchmarkMovingAverageDays = 200; // If the latest benchmark Close is below DMA(BenchmarkMovingAverageDays) then we switch the strategy to the fallback candidates
HoldingPeriod = 3; // 3 is the default. This is in months.
MaxPositions = 3; // 3 is the default
NoTradeSymbols = "GBTC,YOKU,PNY,RFMD,ASAZY"; // ASAZY came up as candidate during 3/30 run but not available on Robinhood
InitialCash = 10000; // The initial cash
TargetBeta = 1.00; // The target Beta used to perform allocation for risk
BetaMonths = 6; // The number of months to use for Beta
MarketCapLowerLimit = 1000000000; // MarketCap lower limit 1,000,000,000
UseMaxBeta = false; // Utilize the MaxBeta filter
MaxBeta = 10.00; // Candidates with Beta that exceed this are rejected.
UseMaxPositionBucketWeight=false; // When set to true then ensure that no single position within a bucket can exceed UseMaxPositionBucketWeightMaxWeight percent
UseMaxPositionBucketWeightMaxWeight=.60; // The maximum weight any single position can be allocated within a bucket. .60 is 60%
UseOverExtendedIndicator=false; // The OverExtendedIndicator uses the upper K band of the Bollinger Band and compares that to the Price.Close on that day. It does this comparison across dayCount days start at TradeDate and going back through time
UseOverExtendedIndicatorDays=10; // This is the number of days of history to scan for OverExtension detetction. 10 gives best results in backtest
UseOverExtendedIndicatorViolationThreshhold=1; // This is the number of items that constitute an upper band break. (i.e.) if this is set to 1 then a single band break is a violation... if 2 then >=2 band breaks are a violation etc., 1.00 gives the best results in backtest
UseOverExtendedIndicatorMarginPercent=1.00; // Add this in so we can control the margin. The best value is 1.00 from backtest results
UseCNN=false; // If set to true then use convolutional network to assist in ranking the candidates
UseCNNHost="http://127.0.0.1:5000"; // The url for the UseCNNHost
UseCNNDayCount=270; // The daycount to use for the image data to present to the convolutional network
UseCNNRewardPercentDecimal=.20; // If a prediction is positive then Score is increased by the specified percentage. Tests using 20% reward show 25% improvement in gains versus not running the CNN
}
public int DayCount { get; set; }
public DateTime AnalysisDate { get; set; }
public DateTime TradeDate { get; set; }
public double DailyReturnLimit { get; set; }
public int MovingAverageConstraintDays { get; set; }
public String FallbackCandidateBestOf { get; set; }
public double FallbackMaxAlloc { get; set; }
public String Benchmark { get; set; }
public int BenchmarkMovingAverageDays { get; set; }
public int HoldingPeriod { get; set; }
public int MaxPositions { get; set; }
public String NoTradeSymbols{ get; set; }
public List<String> NoTradeSymbolsList { get { return null==NoTradeSymbols?null:Utility.ToList(NoTradeSymbols); } }
public double InitialCash { get; set; }
public double TargetBeta { get; set; }
public int BetaMonths { get; set; }
public double MaxBeta { get; set; }
public bool UseMaxBeta { get; set; }
public double MarketCapLowerLimit { get; set; }
public bool UseOverExtendedIndicator{get;set;}
public int UseOverExtendedIndicatorDays { get; set; }
public int UseOverExtendedIndicatorViolationThreshhold { get; set; }
public double UseOverExtendedIndicatorMarginPercent { get; set; }
public bool UseMaxPositionBucketWeight{get;set;}
public double UseMaxPositionBucketWeightMaxWeight{get;set;}
public bool UseCNN{get;set;}
public String UseCNNHost{get;set;}
public int UseCNNDayCount{get;set;}
public double UseCNNRewardPercentDecimal{get;set;}
public void DisplayHeader()
{
MDTrace.WriteLine(LogLevel.DEBUG, "Setting,Value");
}
public NVPCollection ToNVPCollection()
{
NVPCollection nvpCollection = new NVPCollection();
nvpCollection.Add(new NVP("DayCount", DayCount.ToString()));
nvpCollection.Add(new NVP("AnalysisDate", AnalysisDate.ToShortDateString()));
nvpCollection.Add(new NVP("TradeDate", TradeDate.ToShortDateString()));
nvpCollection.Add(new NVP("DailyReturnLimit", DailyReturnLimit.ToString()));
nvpCollection.Add(new NVP("MovingAverageConstraintDays", MovingAverageConstraintDays.ToString()));
nvpCollection.Add(new NVP("FallbackCandidateBestOf", FallbackCandidateBestOf.ToString()));
nvpCollection.Add(new NVP("Benchmark", Benchmark.ToString()));
nvpCollection.Add(new NVP("BenchmarkMovingAverageDays", BenchmarkMovingAverageDays.ToString()));
nvpCollection.Add(new NVP("HoldingPeriod", HoldingPeriod.ToString()));
nvpCollection.Add(new NVP("MaxPositions", MaxPositions.ToString()));
nvpCollection.Add(new NVP("NoTradeSymbols", NoTradeSymbols.ToString()));
nvpCollection.Add(new NVP("InitialCash", InitialCash.ToString()));
nvpCollection.Add(new NVP("TargetBeta", TargetBeta.ToString()));
nvpCollection.Add(new NVP("BetaMonths", BetaMonths.ToString()));
nvpCollection.Add(new NVP("MarketCapLowerLimit", MarketCapLowerLimit.ToString()));
nvpCollection.Add(new NVP("MaxBeta", MaxBeta.ToString()));
nvpCollection.Add(new NVP("UseMaxBeta", UseMaxBeta.ToString()));
nvpCollection.Add(new NVP("FallbackMaxAlloc", FallbackMaxAlloc.ToString()));
nvpCollection.Add(new NVP("UseOverExtendedIndicator",UseOverExtendedIndicator.ToString()));
nvpCollection.Add(new NVP("UseOverExtendedIndicatorDays",UseOverExtendedIndicatorDays.ToString()));
nvpCollection.Add(new NVP("UseOverExtendedIndicatorViolationThreshhold",UseOverExtendedIndicatorViolationThreshhold.ToString()));
nvpCollection.Add(new NVP("UseOverExtendedIndicatorMarginPercent",UseOverExtendedIndicatorMarginPercent.ToString()));
nvpCollection.Add(new NVP("UseMaxPositionBucketWeight",UseMaxPositionBucketWeight.ToString()));
nvpCollection.Add(new NVP("UseMaxPositionBucketWeightMaxWeight",UseMaxPositionBucketWeightMaxWeight.ToString()));
nvpCollection.Add(new NVP("UseCNN",UseCNN.ToString()));
nvpCollection.Add(new NVP("UseCNNHost",UseCNNHost.ToString()));
nvpCollection.Add(new NVP("UseCNNDayCount",UseCNNDayCount.ToString()));
nvpCollection.Add(new NVP("UseCNNRewardPercentDecimal",UseCNNRewardPercentDecimal.ToString()));
return nvpCollection;
}
public static CMParams FromNVPCollection(NVPCollection nvpCollection)
{
CMParams cmParams=new CMParams();
NVPDictionary nvpDictionary = nvpCollection.ToDictionary();
cmParams.DayCount = nvpDictionary["DayCount"].Get<int>();
cmParams.AnalysisDate = nvpDictionary["AnalysisDate"].Get<DateTime>();
cmParams.AnalysisDate = nvpDictionary["TradeDate"].Get<DateTime>();
cmParams.DailyReturnLimit = nvpDictionary["DailyReturnLimit"].Get<double>();
cmParams.MovingAverageConstraintDays = nvpDictionary["MovingAverageConstraintDays"].Get<int>();
cmParams.FallbackCandidateBestOf = nvpDictionary["FallbackCandidateBestOf"].Get<String>();
cmParams.Benchmark = nvpDictionary["Benchmark"].Get<String>();
cmParams.BenchmarkMovingAverageDays = nvpDictionary["BenchmarkMovingAverageDays"].Get<int>();
cmParams.HoldingPeriod = nvpDictionary["HoldingPeriod"].Get<int>();
cmParams.MaxPositions = nvpDictionary["MaxPositions"].Get<int>();
cmParams.NoTradeSymbols = nvpDictionary["NoTradeSymbols"].Get<String>();
cmParams.InitialCash = nvpDictionary["InitialCash"].Get<Double>();
cmParams.TargetBeta = nvpDictionary["TargetBeta"].Get<Double>();
cmParams.BetaMonths = nvpDictionary["BetaMonths"].Get<int>();
cmParams.MarketCapLowerLimit = nvpDictionary["MarketCapLowerLimit"].Get<double>();
cmParams.MaxBeta = nvpDictionary["MaxBeta"].Get<double>();
cmParams.UseMaxBeta = nvpDictionary["UseMaxBeta"].Get<bool>();
cmParams.FallbackMaxAlloc = nvpDictionary["FallbackMaxAlloc"].Get<double>();
if(nvpDictionary.ContainsKey("UseOverExtendedIndicator"))
{
cmParams.UseOverExtendedIndicator=nvpDictionary["UseOverExtendedIndicator"].Get<bool>();
cmParams.UseOverExtendedIndicatorDays=nvpDictionary["UseOverExtendedIndicatorDays"].Get<int>();
cmParams.UseOverExtendedIndicatorViolationThreshhold=nvpDictionary["UseOverExtendedIndicatorViolationThreshhold"].Get<int>();
cmParams.UseOverExtendedIndicatorMarginPercent=nvpDictionary["UseOverExtendedIndicatorMarginPercent"].Get<double>();
}
else
{
cmParams.UseOverExtendedIndicator=false;
cmParams.UseOverExtendedIndicatorDays=0;
cmParams.UseOverExtendedIndicatorViolationThreshhold=1;
cmParams.UseOverExtendedIndicatorMarginPercent=1;
}
if(nvpDictionary.ContainsKey("UseMaxPositionBucketWeight"))
{
cmParams.UseMaxPositionBucketWeight=nvpDictionary["UseMaxPositionBucketWeight"].Get<bool>();
cmParams.UseMaxPositionBucketWeightMaxWeight=nvpDictionary["UseMaxPositionBucketWeightMaxWeight"].Get<double>();
}
else
{
cmParams.UseMaxPositionBucketWeight=true;
cmParams.UseMaxPositionBucketWeightMaxWeight=0.65;
}
if(nvpDictionary.ContainsKey("UseCNN"))
{
cmParams.UseCNN=nvpDictionary["UseCNN"].Get<bool>();
if(nvpDictionary.ContainsKey("UseCNNHost"))cmParams.UseCNNHost=nvpDictionary["UseCNNHost"].Get<String>();
if(nvpDictionary.ContainsKey("UseCNNDayCount"))cmParams.UseCNNDayCount=nvpDictionary["UseCNNDayCount"].Get<int>();
if(nvpDictionary.ContainsKey("UseCNNRewardPercentDecimal"))cmParams.UseCNNRewardPercentDecimal=nvpDictionary["UseCNNRewardPercentDecimal"].Get<double>();
}
return cmParams;
}
public void DisplayConfiguration()
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("DayCount,{0}", DayCount));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("AnalysisDate,{0}", AnalysisDate));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("TradeDate,{0}", TradeDate));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("DailyReturnLimit,{0}", DailyReturnLimit));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("MovingAverageConstraintDays,{0}", MovingAverageConstraintDays));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("FallbackCandidateBestOf,{0}", FallbackCandidateBestOf));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("Benchmark,{0}", Benchmark));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("BenchmarkMovingAverageDays,{0}", BenchmarkMovingAverageDays));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("HoldingPeriod,{0}", HoldingPeriod));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("MaxPositions,{0}", MaxPositions));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("NoTradeSymbols,{0}", NoTradeSymbols));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("InitialCash,{0}", InitialCash));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("TargetBeta,{0}", TargetBeta.ToString()));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("BetaMonths,{0}", BetaMonths.ToString()));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("MarketCapLowerLimit,{0}", MarketCapLowerLimit.ToString()));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("UseMaxBeta,{0}", UseMaxBeta.ToString()));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("MaxBeta,{0}", MaxBeta.ToString()));
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("FallbackMaxAlloc,{0}", FallbackMaxAlloc.ToString()));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseOverExtendedIndicator,{0}",UseOverExtendedIndicator.ToString()));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseOverExtendedIndicatorDays,{0}",UseOverExtendedIndicatorDays.ToString()));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseOverExtendedIndicatorViolationThreshhold,{0}",UseOverExtendedIndicatorViolationThreshhold.ToString()));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseOverExtendedIndicatorMarginPercent,{0}",UseOverExtendedIndicatorMarginPercent.ToString()));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseMaxPositionBucketWeight,{0}",UseMaxPositionBucketWeight.ToString()));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseMaxPositionBucketWeightMaxWeight,{0}",UseMaxPositionBucketWeightMaxWeight.ToString()));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseCNN,{0}",UseCNN.ToString()));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseCNNHost,{0}",UseCNNHost.ToString()));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseCNNDayCount,{0}",UseCNNDayCount.ToString()));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("UseCNNRewardPercentDecimal,{0}",UseCNNRewardPercentDecimal.ToString()));
}
}
}

View File

@@ -0,0 +1,282 @@
using MarketData.Utils;
using MarketData.Generator.Interface;
namespace MarketData.Generator.CMMomentum
{
public class Position : IPurePosition
{
public Position()
{
CurrentPrice = double.NaN;
CNNPrediction=false;
}
public Position(Position position)
{
Symbol = position.Symbol;
PurchaseDate = position.PurchaseDate;
SellDate = position.SellDate;
Shares = position.Shares;
PurchasePrice = position.PurchasePrice;
CurrentPrice = position.CurrentPrice;
Beta = position.Beta;
BetaMonths = position.BetaMonths;
SharpeRatio = position.SharpeRatio;
Weight = position.Weight;
RiskAdjustedWeight = position.RiskAdjustedWeight;
RiskAdjustedAllocation = position.RiskAdjustedAllocation;
TargetBetaOverBeta = position.TargetBetaOverBeta;
CNNPrediction=position.CNNPrediction;
}
public static Position Clone(Position position)
{
Position clonedPosition = new Position();
clonedPosition.Symbol = position.Symbol;
clonedPosition.PurchaseDate = position.PurchaseDate;
clonedPosition.SellDate = position.SellDate;
clonedPosition.Shares = position.Shares;
clonedPosition.PurchasePrice = position.PurchasePrice;
clonedPosition.CurrentPrice = position.CurrentPrice;
clonedPosition.Beta = position.Beta;
clonedPosition.BetaMonths = position.BetaMonths;
clonedPosition.SharpeRatio = position.SharpeRatio;
clonedPosition.Weight = position.Weight;
clonedPosition.RiskAdjustedWeight = position.RiskAdjustedWeight;
clonedPosition.RiskAdjustedAllocation = position.RiskAdjustedAllocation;
clonedPosition.TargetBetaOverBeta = position.TargetBetaOverBeta;
clonedPosition.CNNPrediction = position.CNNPrediction;
return clonedPosition;
}
public String Symbol { get; set; }
public DateTime PurchaseDate { get; set; }
public DateTime SellDate { get; set; }
public double Shares { get; set; }
public double PurchasePrice { get; set; }
public double CurrentPrice { get; set; }
public double Exposure { get { return Shares * PurchasePrice; } } // Derived
public double MarketValue { get { return Shares * CurrentPrice; } } // Derived
public double GainLoss { get { return MarketValue - Exposure; } } // Derived
public double GainLossPcnt { get { return (MarketValue - Exposure) / Exposure; } }// Derived
public double Beta { get; set; }
public int BetaMonths { get; set; }
public double SharpeRatio { get; set; }
public double Weight { get; set; }
public double RiskAdjustedWeight { get; set; }
public double RiskAdjustedAllocation { get; set; }
public double TargetBetaOverBeta { get; set; }
public double Score{get;set;}
public bool CNNPrediction{get;set;}
public virtual NVPCollection ToNVPCollection()
{
NVPCollection nvpCollection = new NVPCollection();
nvpCollection.Add(new NVP("Symbol", Symbol.ToString()));
nvpCollection.Add(new NVP("PurchaseDate", PurchaseDate.Date.ToString()));
nvpCollection.Add(new NVP("SellDate", SellDate.Date.ToString()));
nvpCollection.Add(new NVP("Shares", Shares.ToString()));
nvpCollection.Add(new NVP("PurchasePrice", PurchasePrice.ToString()));
nvpCollection.Add(new NVP("Beta", Beta.ToString()));
nvpCollection.Add(new NVP("BetaMonths", BetaMonths.ToString()));
nvpCollection.Add(new NVP("SharpeRatio", SharpeRatio.ToString()));
nvpCollection.Add(new NVP("RiskAdjustedWeight", RiskAdjustedWeight.ToString()));
nvpCollection.Add(new NVP("RiskAdjustedAllocation", RiskAdjustedAllocation.ToString()));
nvpCollection.Add(new NVP("TargetBetaOverBeta", TargetBetaOverBeta.ToString()));
if(!double.IsNaN(CurrentPrice))nvpCollection.Add(new NVP("CurrentPrice", CurrentPrice.ToString()));
nvpCollection.Add(new NVP("Score", Score.ToString()));
nvpCollection.Add(new NVP("CNNPrediction", CNNPrediction.ToString()));
return nvpCollection;
}
public static Position FromNVPCollection(NVPCollection nvpCollection)
{
Position position = new Position();
NVPDictionary nvpDictionary = nvpCollection.ToDictionary();
position.Symbol = nvpDictionary["Symbol"].Get<String>();
position.PurchaseDate = nvpDictionary["PurchaseDate"].Get<DateTime>().Date;
position.SellDate = nvpDictionary["SellDate"].Get<DateTime>().Date;
position.Shares = nvpDictionary["Shares"].Get<double>();
position.PurchasePrice = nvpDictionary["PurchasePrice"].Get<double>();
if(nvpDictionary.ContainsKey("CurrentPrice"))position.CurrentPrice = nvpDictionary["CurrentPrice"].Get<double>();
position.Beta = nvpDictionary["Beta"].Get<double>();
position.BetaMonths = nvpDictionary["BetaMonths"].Get<int>();
position.SharpeRatio = nvpDictionary["SharpeRatio"].Get<double>();
position.RiskAdjustedWeight = nvpDictionary["RiskAdjustedWeight"].Get<double>();
position.RiskAdjustedAllocation = nvpDictionary["RiskAdjustedAllocation"].Get<double>();
position.TargetBetaOverBeta = nvpDictionary["TargetBetaOverBeta"].Get<double>();
if(nvpDictionary.ContainsKey("Score"))position.Score = nvpDictionary["Score"].Get<double>();
if(nvpDictionary.ContainsKey("CNNPrediction"))position.CNNPrediction = nvpDictionary["CNNPrediction"].Get<bool>();
return position;
}
public void Display()
{
if (Utility.IsEpoch(SellDate) && double.IsNaN(CurrentPrice))
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17}",
Symbol,
Shares,
Utility.DateTimeToStringMMHDDHYYYY(PurchaseDate),
Utility.AddQuotes(Utility.FormatCurrency(PurchasePrice)),
Utility.IsEpoch(SellDate) ? "N/A" : Utility.DateTimeToStringMMHDDHYYYY(SellDate),
"N/A",
Utility.AddQuotes(Utility.FormatCurrency(Exposure)),
Utility.AddQuotes(Utility.FormatNumber(Beta, 4)),
Utility.AddQuotes(Utility.FormatNumber(BetaMonths)),
Utility.AddQuotes(Utility.FormatNumber(SharpeRatio, 4)),
Utility.AddQuotes(Utility.FormatNumber(RiskAdjustedWeight)),
Utility.AddQuotes(Utility.FormatNumber(RiskAdjustedAllocation)),
Utility.AddQuotes(Utility.FormatNumber(TargetBetaOverBeta)),
Utility.AddQuotes(Utility.FormatNumber(Score,3)),
Utility.AddQuotes(CNNPrediction.ToString()),
"N/A",
"N/A",
"N/A"
));
}
else
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17}",
Symbol,
Shares,
Utility.DateTimeToStringMMHDDHYYYY(PurchaseDate),
Utility.AddQuotes(Utility.FormatCurrency(PurchasePrice)),
Utility.IsEpoch(SellDate) ? "N/A" : Utility.DateTimeToStringMMHDDHYYYY(SellDate),
Utility.AddQuotes(Utility.FormatCurrency(CurrentPrice)),
Utility.AddQuotes(Utility.FormatCurrency(Exposure)),
Utility.AddQuotes(Utility.FormatNumber(Beta, 4)),
Utility.AddQuotes(Utility.FormatNumber(BetaMonths)),
Utility.AddQuotes(Utility.FormatNumber(SharpeRatio, 4)),
Utility.AddQuotes(Utility.FormatNumber(RiskAdjustedWeight)),
Utility.AddQuotes(Utility.FormatNumber(RiskAdjustedAllocation)),
Utility.AddQuotes(Utility.FormatNumber(TargetBetaOverBeta)),
Utility.AddQuotes(Utility.FormatNumber(Score,3)),
Utility.AddQuotes(CNNPrediction.ToString()),
Utility.AddQuotes(Utility.FormatCurrency(MarketValue)),
Utility.AddQuotes(Utility.FormatCurrency(GainLoss)),
Utility.FormatPercent(GainLossPcnt)
));
}
}
public static void DisplayHeader()
{
MDTrace.WriteLine(LogLevel.DEBUG, "Symbol,Shares,Purchase Date,Purchase Price,Sell Date,Sell Price,Exposure,Beta,BetaMonths,SharpeRatio,RiskAdjustedWeight,RiskAdjustedAllocation,TargetBetaOverBeta,Score,CNNPrediction,Market Value,Gain Loss,Gain Loss(%)");
}
}
// ****************************************************************************************************************************************************************
public class Positions : List<Position>
{
public Positions()
{
}
public Positions(Positions positions)
{
foreach (Position position in positions) Add(position);
}
public Positions(List<Position> positions)
{
foreach (Position position in positions) Add(position);
}
public Positions(Position position)
{
Add(position);
}
public void Add(Positions positions)
{
foreach (Position position in positions) Add(position);
}
public double Exposure
{
get
{
return (from Position position in this select position.PurchasePrice * position.Shares).Sum();
}
}
public double MarketValue
{
get
{
return (from Position position in this select position.CurrentPrice * position.Shares).Sum();
}
}
public NVPCollections ToNVPCollections()
{
NVPCollections nvpCollections = new NVPCollections();
foreach (Position position in this)
{
nvpCollections.Add(position.ToNVPCollection());
}
return nvpCollections;
}
public static Positions FromNVPCollections(NVPCollections nvpCollections)
{
Positions positions = new Positions();
foreach (NVPCollection nvpCollection in nvpCollections)
{
positions.Add(Position.FromNVPCollection(nvpCollection));
}
return positions;
}
public void DisplayTopFive()
{
Positions positions = new Positions(this.OrderByDescending(x => x.GainLossPcnt).Take(5).ToList());
Position.DisplayHeader();
for (int index = 0; index < positions.Count; index++)
{
Position position = positions[index];
position.Display();
}
}
public void DisplayBottomFive()
{
Positions positions = new Positions(this.OrderBy(x => x.GainLossPcnt).Take(5).ToList());
Position.DisplayHeader();
for (int index = 0; index < positions.Count; index++)
{
Position position = positions[index];
position.Display();
}
}
public void Display()
{
Position.DisplayHeader();
for (int index = 0; index < Count; index++)
{
Position position = this[index];
position.Display();
}
MDTrace.WriteLine(LogLevel.DEBUG, "****************************************************************************************************************************");
}
}
}

View File

@@ -0,0 +1,186 @@
using MarketData.Utils;
namespace MarketData.Generator.CMMomentum
{
public class CMSessionParams
{
public DateTime LastUpdated { get; set; }
public DateTime TradeDate { get; set; }
public DateTime StartDate { get; set; }
public DateTime AnalysisDate { get; set; }
public CMParams CMParams { get; set; }
public ActivePositions ActivePositions { get; set; }
public Positions AllPositions { get; set; }
public double CashBalance { get; set; }
public double NonTradeableCash { get; set; }
public int Cycle { get; set; }
// This gets AllPositions+Positions
public Positions GetCombinedPositions()
{
Positions positions=new Positions();
foreach(Position position in AllPositions) positions.Add(position);
Positions activePositions=ActivePositions.GetPositions();
foreach(Position position in activePositions) positions.Add(position);
return positions;
}
}
// *****************************************************************************
public class CMSessionManager
{
private static String SIGNATURE="CMSESSIONv1.00";
public bool SaveSession(CMSessionParams sessionParams, String pathSessionFile)
{
try
{
if (null == pathSessionFile) return false;
pathSessionFile = GetSessionFileName(pathSessionFile);
FileStream outStream = new FileStream(pathSessionFile, FileMode.Create);
StreamWriter streamWriter = new StreamWriter(outStream);
streamWriter.WriteLine(SIGNATURE);
streamWriter.WriteLine((new NVP("LastUpdated", sessionParams.LastUpdated.ToString())).ToString());
streamWriter.WriteLine((new NVP("TradeDate", sessionParams.TradeDate.ToShortDateString())).ToString());
streamWriter.WriteLine((new NVP("StartDate", sessionParams.StartDate.ToShortDateString())).ToString());
streamWriter.WriteLine((new NVP("AnalysisDate", sessionParams.AnalysisDate.ToShortDateString())).ToString());
streamWriter.WriteLine((new NVP("Cycle", sessionParams.Cycle.ToString())).ToString());
streamWriter.WriteLine((new NVP("CashBalance", sessionParams.CashBalance.ToString())).ToString());
streamWriter.WriteLine((new NVP("NonTradeableCash", sessionParams.NonTradeableCash.ToString())).ToString());
NVPCollection configurationCollection = sessionParams.CMParams.ToNVPCollection();
streamWriter.WriteLine(configurationCollection.ToString());
List<NVPCollections> nvpCollectionsList = sessionParams.ActivePositions.ToNVPCollections();
int totalPositions = 0;
foreach (NVPCollections nvpCollections in nvpCollectionsList)
{
List<String> nvpCollectionsStringList = nvpCollections.ToList();
totalPositions += nvpCollectionsStringList.Count;
}
streamWriter.WriteLine((new NVP("TotalActivePositions", totalPositions.ToString())).ToString());
foreach (NVPCollections nvpCollections in nvpCollectionsList)
{
List<String> nvpCollectionsStringList = nvpCollections.ToList();
foreach (String str in nvpCollectionsStringList)
{
streamWriter.WriteLine(str);
}
}
NVPCollections allPositionsCollections = sessionParams.AllPositions.ToNVPCollections();
List<String> nvpAllCollectionsStringList = allPositionsCollections.ToList();
streamWriter.WriteLine((new NVP("TotalPositions", nvpAllCollectionsStringList.Count.ToString())).ToString());
foreach (String str in nvpAllCollectionsStringList) streamWriter.WriteLine(str);
streamWriter.Flush();
outStream.Flush();
streamWriter.Close();
streamWriter.Dispose();
outStream.Close();
outStream.Dispose();
return true;
}
catch (Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG, String.Format("{0}", exception.ToString()));
return false;
}
}
public static CMSessionParams RestoreSession(String pathSessionFile)
{
FileStream inStream = null;
StreamReader streamReader = null;
try
{
pathSessionFile = GetSessionFileName(pathSessionFile);
if (!SessionAvailable(pathSessionFile)) return null;
CMSessionParams sessionParams = new CMSessionParams();
inStream = new FileStream(pathSessionFile, FileMode.Open);
streamReader = new StreamReader(inStream);
String versionInfo = streamReader.ReadLine();
if (!versionInfo.StartsWith(SIGNATURE)) return null;
double version = double.Parse(Utility.BetweenString(versionInfo, "v", null));
if (1.00 != version) return null;
NVP lastUpdated = new NVP(streamReader.ReadLine());
NVP tradeDate = new NVP(streamReader.ReadLine());
NVP startDate = new NVP(streamReader.ReadLine());
NVP analysisDate = new NVP(streamReader.ReadLine());
NVP cycle = new NVP(streamReader.ReadLine());
NVP cashBalance = new NVP(streamReader.ReadLine());
NVP nonTradeableCash = new NVP(streamReader.ReadLine());
sessionParams.LastUpdated = lastUpdated.Get<DateTime>();
sessionParams.TradeDate = tradeDate.Get<DateTime>();
sessionParams.StartDate = startDate.Get<DateTime>();
sessionParams.AnalysisDate = analysisDate.Get<DateTime>();
sessionParams.Cycle = cycle.Get<int>();
sessionParams.CashBalance = cashBalance.Get<double>();
sessionParams.NonTradeableCash=nonTradeableCash.Get<double>();
NVPCollection configurationCollection = new NVPCollection(streamReader.ReadLine());
sessionParams.CMParams = CMParams.FromNVPCollection(configurationCollection);
int totalActivePositions = (new NVP(streamReader.ReadLine())).Get<int>();
sessionParams.ActivePositions = new ActivePositions();
for (int positionIndex = 0; positionIndex < totalActivePositions; positionIndex++)
{
NVPCollection nvpCollection = new NVPCollection(streamReader.ReadLine());
sessionParams.ActivePositions.AddFromNVPCollection(nvpCollection);
}
int totalPositions = (new NVP(streamReader.ReadLine())).Get<int>();
NVPCollections nvpCollections = new NVPCollections();
for (int positionIndex = 0; positionIndex < totalPositions; positionIndex++)
{
NVPCollection nvpCollection = new NVPCollection(streamReader.ReadLine());
nvpCollections.Add(nvpCollection);
}
sessionParams.AllPositions = Positions.FromNVPCollections(nvpCollections);
inStream.Close();
inStream.Dispose();
inStream = null;
streamReader.Close();
streamReader.Dispose();
streamReader = null;
return sessionParams;
}
finally
{
if (null != streamReader) streamReader.Close();
if (null != inStream) inStream.Close();
}
}
public static String GetSessionFileName(String pathSessionFile)
{
if(null==pathSessionFile) return null;
String directory=Path.GetDirectoryName(pathSessionFile);
if("".Equals(directory)) directory=Directory.GetCurrentDirectory();
return directory+"/"+Path.GetFileNameWithoutExtension(pathSessionFile)+".TXT";
}
public static bool SessionAvailable(String pathSessionFile)
{
return IsValidSessionFile(pathSessionFile);
}
public static bool IsValidSessionFile(String pathSessionFile)
{
FileStream inStream = null;
StreamReader streamReader = null;
try
{
if (null == pathSessionFile) return false;
pathSessionFile = GetSessionFileName(pathSessionFile);
if (!File.Exists(pathSessionFile)) return false;
inStream = new FileStream(pathSessionFile, FileMode.Open);
streamReader = new StreamReader(inStream);
String versionInfo = streamReader.ReadLine();
if (!versionInfo.StartsWith(SIGNATURE)) return false;
double version = double.Parse(Utility.BetweenString(versionInfo, "v", null));
if (1.00 != version) return false;
return true;
}
catch (Exception)
{
return false;
}
finally
{
if (null != streamReader) streamReader.Close();
if (null != inStream) inStream.Close();
}
}
}
}

View File

@@ -0,0 +1,110 @@
using MarketData.Utils;
namespace MarketData.Generator.CMMomentum
{
public class SlotPositions : List<SlotPosition>
{
public SlotPositions()
{
}
public SlotPositions(int slot, Positions positions)
{
for (int index = 0; index < positions.Count; index++)
{
Add(new SlotPosition(slot, positions[index]));
}
}
public NVPCollections ToNVPCollections()
{
NVPCollections nvpCollections = new NVPCollections();
foreach (SlotPosition position in this)
{
nvpCollections.Add(position.ToNVPCollection());
}
return nvpCollections;
}
public static SlotPositions FromNVPCollections(NVPCollections nvpCollections)
{
SlotPositions positions = new SlotPositions();
foreach (NVPCollection nvpCollection in nvpCollections)
{
positions.Add(SlotPosition.FromNVPCollection(nvpCollection));
}
return positions;
}
}
public class SlotPosition : Position
{
public SlotPosition()
{
}
public SlotPosition(int slot, Position position) : base(position)
{
Slot = slot;
//Symbol = position.Symbol;
//PurchaseDate = position.PurchaseDate;
//SellDate = position.SellDate;
//Shares = position.Shares;
//PurchasePrice = position.PurchasePrice;
//CurrentPrice = position.CurrentPrice;
//Beta= position.Beta;
//BetaMonths = position.BetaMonths;
//SharpeRatio = position.SharpeRatio;
//Weight = position.Weight;
//RiskAdjustedWeight = position.RiskAdjustedWeight;
//RiskAdjustedAllocation = position.RiskAdjustedAllocation;
//TargetBetaOverBeta = position.TargetBetaOverBeta;
}
public SlotPosition(Position position) : base(position)
{
//Symbol = position.Symbol;
//PurchaseDate = position.PurchaseDate;
//SellDate = position.SellDate;
//Shares = position.Shares;
//PurchasePrice = position.PurchasePrice;
//CurrentPrice = position.CurrentPrice;
//Beta = position.Beta;
//BetaMonths = position.BetaMonths;
//SharpeRatio = position.SharpeRatio;
//Weight = position.Weight;
//RiskAdjustedWeight = position.RiskAdjustedWeight;
//RiskAdjustedAllocation = position.RiskAdjustedAllocation;
//TargetBetaOverBeta = position.TargetBetaOverBeta;
}
public Position ToPosition()
{
Position position = new Position(this);
return position;
//Position position = new Position();
//position.Symbol = Symbol;
//position.PurchaseDate = PurchaseDate;
//position.SellDate = SellDate;
//position.Shares = Shares;
//position.PurchasePrice = PurchasePrice;
//position.CurrentPrice = CurrentPrice;
//position.Beta = Beta;
//position.BetaMonths = BetaMonths;
//position.SharpeRatio = SharpeRatio;
//position.Weight = Weight;
//position.RiskAdjustedWeight = RiskAdjustedWeight;
//position.RiskAdjustedAllocation = RiskAdjustedAllocation;
//position.TargetBetaOverBeta = TargetBetaOverBeta;
//return position;
}
public int Slot { get; set; }
public override NVPCollection ToNVPCollection()
{
NVPCollection nvpCollection = base.ToNVPCollection();
nvpCollection.Insert(0, new NVP("Slot", Slot.ToString()));
return nvpCollection;
}
public static new SlotPosition FromNVPCollection(NVPCollection nvpCollection)
{
NVPDictionary nvpDictionary = nvpCollection.ToDictionary();
Position position = Position.FromNVPCollection(nvpCollection);
SlotPosition slotPosition = new SlotPosition(position);
slotPosition.Slot = nvpDictionary["Slot"].Get<int>();
return slotPosition;
}
}
}

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MarketData.Generator.Model
{
public class RealtimeGainLoss
{
public double Exposure{get;set;}
public double MarketValue{get;set;}
public double GainLoss{get{return MarketValue-Exposure;}}
public double GainLossPercent{get{return Exposure==0?0:(MarketValue-Exposure)/Exposure;}}
}
}