Files
marketdata/MarketDataLib/Generator/CMMomentum/CMMomentumGenerator.cs
Sean 2882559651 Fix GetMonthlyPrices in Prices. It was not correctly returning monthly prices.
This was causing an issue in the SharpeRatioGenerator whereby the SharpeRatio was not being calculated correctly where the asof date would
fall on a weekend of holiday.
Also, added a ReasonCategory to the CMCanidate as well as a Violation summary line in the model output to show where violations occur.
Had I implemented this previously I might have detected the SharpeRatio issue sooner.
Also added GetFundamentalMaxDateTop in the FundamentalDA which I used during debugging but is not currently being used anywhere.
2025-02-02 14:59:14 -05:00

165 lines
8.5 KiB
C#

using MarketData.CNNProcessing;
using MarketData.DataAccess;
using MarketData.Generator.MovingAverage;
using MarketData.MarketDataModel;
using MarketData.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
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;
}
}
}
}