199 lines
10 KiB
C#
199 lines
10 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.IO;
|
|
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;
|
|
// }
|
|
//}
|
|
|
|
// 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();
|
|
int imageDimensions=224;
|
|
dataProcessor.Width=imageDimensions;
|
|
dataProcessor.Height=imageDimensions;
|
|
dataProcessor.PenWidth=1;
|
|
TestCase testCase=new TestCase(cmCandidate.Symbol,cmCandidate.TradeDate,cmParams.UseCNNDayCount,TestCase.CaseType.Test,TestCase.GenerateType.BollingerBandWithVIX,TestCase.OutputType.OutputStream);
|
|
dataProcessor.ProcessData(testCase);
|
|
Stream streamResult = cnnClient.ProcessImage(testCase.LastStream); // process the image through PIL
|
|
String prediction = cnnClient.Predict(CNNClient.Model.convnext,streamResult);
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|