Add the MGSHMomentumHelper

This commit is contained in:
2025-02-06 16:50:01 -05:00
parent 0a5c5a9594
commit 47ccedf1fb
4 changed files with 388 additions and 13 deletions

View File

@@ -19,6 +19,6 @@
<add key="proxy_GetDividendHistory" value="false"/>
<add key="proxy_GetAnalystPriceTargetMarketBeat" value="false"/>
<add key="proxy_GetCompanyHeadlinesSeekingAlphaV1" value="true"/>
<add key="proxy_GetCompanyHeadlinesSeekingAlphaV2" value="true"/>
<add key="proxy_GetCompanyHeadlinesSeekingAlphaV2" value="true"/>
</appSettings>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/></startup></configuration>

127
MGSHMomentumHelper.cs Normal file
View File

@@ -0,0 +1,127 @@
using MarketData.Generator.MGSHMomentum;
using System;
using System.Collections.Generic;
namespace MarketData
{
public static class MGSHMomentumHelper
{
public static void HandleMGSHSession(String[] args)
{
CommandArgs commandArgs = new CommandArgs(args);
if(!commandArgs.Has("SESSIONFILE"))
{
MDTrace.WriteLine(LogLevel.DEBUG,"Missing SESSIONFILE");
return;
}
MGSHMomentumBacktest momentumBacktest = new MGSHMomentumBacktest();
momentumBacktest.DisplaySession(commandArgs.Coalesce<String>("SESSIONFILE"));
}
public static void HandleMGSHRunDaily(String[] args)
{
CommandArgs commandArgs = new CommandArgs(args);
if(!commandArgs.Has("SESSIONFILE,TRADEDATE"))
{
MDTrace.WriteLine(LogLevel.DEBUG,"SESSIONFILE and TRADEDATE are required parameters.");
return;
}
DateTime tradeDate = commandArgs.Get<DateTime>("TRADEDATE");
String pathSessionFile = commandArgs.Get<String>("PATHSESSIONFILE");
MGSHMomentumBacktest momentumBacktest = new MGSHMomentumBacktest();
MGSHBacktestResult backtestResult = momentumBacktest.UpdateDaily(tradeDate, tradeDate, pathSessionFile);
backtestResult.Display();
}
public static void HandleMGSHRunBacktest(String[] args)
{
CommandArgs commandArgs = new CommandArgs(args);
MGSHConfiguration mgParams=new MGSHConfiguration();
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;
}
mgParams.MaxPositions=commandArgs.Coalesce<int>("MAXPOSITIONS");
mgParams.InitialCash=commandArgs.Coalesce<int>("INITIALCASH");
mgParams.HoldingPeriod=commandArgs.Coalesce<int>("HOLDINGPERIOD");
if(commandArgs.Has("INCLUDETRADEMASTERFORSYMBOLSHELD"))
{
mgParams.IncludeTradeMasterForSymbolsHeld=commandArgs.Get<bool>("INCLUDETRADEMASTERFORSYMBOLSHELD");
}
if(commandArgs.Has("USESTOCHASTICS"))
{
mgParams.UseStochastics=commandArgs.Coalesce<bool>("USESTOCHASTICS",true);
}
if(commandArgs.Has("USESTOPLIMITS"))
{
mgParams.UseStopLimits=commandArgs.Coalesce<bool>("USESTOPLIMITS",false);
}
if(commandArgs.Has("STOPLIMITRISKPERCENTDECIMAL"))
{
mgParams.StopLimitRiskPercentDecimal=commandArgs.Coalesce<double>("STOPLIMITRISKPERCENTDECIMAL",.12);
}
if(commandArgs.Has("USEHEDGING"))
{
mgParams.UseHedging=commandArgs.Coalesce<bool>("USEHEDGING",false);
if(commandArgs.Has("INITIALHEDGECASH"))
{
mgParams.HedgeInitialCash=commandArgs.Get<double>("INITIALHEDGECASH");
}
}
if(commandArgs.Has("KEEPSLOTPOSITIONS"))
{
mgParams.KeepSlotPositions=commandArgs.Get<bool>("KEEPSLOTPOSITIONS");
}
// ** M A C D
if(commandArgs.Has("USEMACD"))
{
mgParams.UseMACD=commandArgs.Coalesce<bool>("USEMACD",true);
}
if(commandArgs.Has("MACDREJECTSTRONGSELLSIGNALS"))
{
mgParams.MACDRejectStrongSellSignals=commandArgs.Coalesce<bool>("MACDREJECTSTRONGSELLSIGNALS",true);
}
if(commandArgs.Has("MACDREJECTWEAKSELLSIGNALS"))
{
mgParams.MACDRejectWeakSellSignals=commandArgs.Coalesce<bool>("MACDREJECTWEAKSELLSIGNALS",true);
}
if(commandArgs.Has("MACDSIGNALDAYS"))
{
mgParams.MACDSignalDays=commandArgs.Coalesce<int>("MACDSIGNALDAYS",mgParams.MACDSignalDays);
}
if(commandArgs.Has("MACDSETUP"))
{
mgParams.MACDSetup=commandArgs.Coalesce<String>("MACDSETUP",mgParams.MACDSetup);
}
// **
MGSHQualityIndicator qualityIndicator=new MGSHQualityIndicator(MGSHQualityIndicator.QualityType.IDIndicator);
if(commandArgs.Has("QUALITYINDICATORTYPE")) qualityIndicator.Quality=MGSHQualityIndicator.ToQuality(commandArgs.Coalesce<String>("QUALITYINDICATORTYPE","IDINDICATOR"));
mgParams.QualityIndicatorType=qualityIndicator.ToString();
mgParams.UseLowSlopeBetaCheck=true;
if(commandArgs.Has("USELOWSLOPEBETACHECK")) mgParams.UseLowSlopeBetaCheck=commandArgs.Coalesce<bool>("USELOWSLOPEBETACHECK",true);
DateTime startDate = commandArgs.Coalesce<DateTime>("STARTDATE");
DateTime endDate=commandArgs.Coalesce<DateTime>("ENDDATE",new DateTime());
String pathSessionFileName = commandArgs.Coalesce<String>("SESSIONFILE", null);
if(null!=pathSessionFileName)pathSessionFileName=pathSessionFileName.Trim();
mgParams.DisplayHeader();
List<MGSHBacktestResult> results=new List<MGSHBacktestResult>();
MGSHMomentumBacktest backtestMomentum=new MGSHMomentumBacktest();
results.Add(backtestMomentum.PerformBacktest(startDate,endDate,pathSessionFileName,mgParams));
}
}
}

View File

@@ -72,6 +72,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="CommandArgs.cs" />
<Compile Include="MGSHMomentumHelper.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>

View File

@@ -25,6 +25,7 @@ using System.Data;
using MarketData.CNNProcessing;
using MySql.Data.MySqlClient;
using MarketData.Generator.MovingAverage;
using MarketData.Generator.MGSHMomentum;
namespace MarketData
{
@@ -190,6 +191,10 @@ namespace MarketData
MDTrace.WriteLine(LogLevel.DEBUG," MGCLOSEPOSITION /SESSIONFILE: /SYMBOL: /PURCHASEDATE: /SELLPRICE: /SELLDATE: sells the specified position in the session file with either the supplied sell date and sell price.");
MDTrace.WriteLine(LogLevel.DEBUG," MGUPDATEPRICE /SYMBOL: /TRADEDATE: /PRICE: /SESSIONFILE:");
MDTrace.WriteLine(LogLevel.DEBUG," RUNBACKTEST /STARTDATE: /MAXPOSITIONS: /INITIALCASH: /HOLDINGPERIOD: /{ENDDATE}: /SESSIONFILE:");
MDTrace.WriteLine(LogLevel.DEBUG,"********** M G S H M O M E N T U M *********");
MDTrace.WriteLine(LogLevel.DEBUG," MGSHSESSION /SESSIONFILE:");
MDTrace.WriteLine(LogLevel.DEBUG," MGSHRUNBACKTEST /USEHEDGING: /HEDGEINITIALCASH: /USESTOPLIMITS: /KEEPSLOTPOSITIONS: /STARTDATE: /MAXPOSITIONS: /INITIALCASH: /HOLDINGPERIOD: /{ENDDATE}: /SESSIONFILE: ");
MDTrace.WriteLine(LogLevel.DEBUG," MGSHRUNDAILY /SESSIONFILE: /TRADEDATE:");
MDTrace.WriteLine(LogLevel.DEBUG, "*********** C L E N O W M O M E N T U M (C M M O M E N T U M )************");
MDTrace.WriteLine(LogLevel.DEBUG, " RUNCMBACKTEST /STARTDATE: /MAXPOSITIONS: /INITIALCASH: /HOLDINGPERIOD: /{USEBINBASEDPOSITIONSIZING}: /{USEBINBASEDPOSITIONSIZINGNUMBINS}: /{TARGETBETA}: /{ENDDATE}: /SESSIONFILE: /{USECNN}: /{USECNNHOST}: /{USECNNDAYCOUNT}:");
MDTrace.WriteLine(LogLevel.DEBUG, " CMSESSION /SESSIONFILE:");
@@ -758,6 +763,212 @@ namespace MarketData
outStream.Close();
outStream.Dispose();
}
// *****************************************************************************************************************************************************
public static void HedgeTest()
{
String hedgeShortSymbol="SH";
double initialRiskPercentDecimal=.12;
double cash=10000;
double cashToInvest=3000;
double totalGainLoss=0.00;
bool hedgeOn=false;
int totalWinners=0;
int totalLosers=0;
int hedgeStopLimitScalingVolatilityDays=14;
int hedgeMinDaysBetweenStopAdjustments=1;
MGSHPosition position = default;
MDTrace.WriteLine(LogLevel.DEBUG,"START");
DateTime analysisStartDate=DateTime.Parse("01/31/2018");
// DateTime analysisStartDate=DateTime.Parse("01/01/2011");
// DateTime analysisStartDate=DateTime.Parse("06/06/2018");
DateTime analysisEndDate=DateTime.Parse("12/31/2024");
Prices prices = PricingDA.GetPrices(hedgeShortSymbol);
prices=new Prices(prices.Where(x => x.Date > analysisStartDate).ToList());
BollingerBands bollingerBands=BollingerBandGenerator.GenerateBollingerBands(prices);
BollingerBandElementsByDate bollingerBandElementsByDate = bollingerBands.GetBollingerBandElementsByDate();
PricesByDate pricesByDate = prices.GetPricesByDate();
List<DateTime> dates = new List<DateTime>(pricesByDate.Keys);
MGSHPositions AllPositions = new MGSHPositions();
MarketData.Generator.Model.StopLimits StopLimits = new MarketData.Generator.Model.StopLimits();
HedgeManager hedgeManager = new HedgeManager(){Verbose=true};
for(int dateIndex=dates.Count-1;dateIndex>=0;dateIndex--)
{
DateTime analysisDate = dates[dateIndex];
if(analysisDate > analysisEndDate)break;
if(!bollingerBandElementsByDate.ContainsKey(analysisDate))continue;
BollingerBandElement bollingerBandElement = bollingerBandElementsByDate[analysisDate];
if(!hedgeOn && hedgeManager.IsOpenHedgeIndicator(analysisDate))
{
hedgeOn=true;
position = new MGSHPosition();
position.Symbol=hedgeShortSymbol;
position.PurchasePrice = bollingerBandElement.Close;
position.PurchaseDate = analysisDate;
position.SellDate=Utility.Epoch;
position.InitialStopLimit=position.PurchasePrice/(1.00+initialRiskPercentDecimal);
position.TrailingStopLimit=position.InitialStopLimit;
position.LastStopAdjustment=Utility.Epoch;
position.R=initialRiskPercentDecimal;
double cashAllocation = Math.Min(cash, cashToInvest);
position.Shares=(int)(cashAllocation/bollingerBandElement.Close);
cash-=position.Shares*bollingerBandElement.Close;
// This is for the simulation so that I can see the initial position as a stop limit in the UI
MarketData.Generator.Model.StopLimit stopLimit = new MarketData.Generator.Model.StopLimit()
{
Symbol=hedgeShortSymbol,
AnalysisDate=analysisDate,
PreviousStop=position.PurchasePrice,
NewStop=position.TrailingStopLimit,
CurrentPriceLow=0.00,
CurrentPriceClose=0.00,
PriceTrendIndicatorSlope=0.00
};
StopLimits.Add(stopLimit);
MDTrace.WriteLine(LogLevel.DEBUG,$"Hedge BUY {analysisDate.ToShortDateString()} {Utility.FormatNumber(position.Shares,2)} shares @ {Utility.FormatCurrency(bollingerBandElement.Close)} Exposure:{Utility.FormatCurrency(position.Shares*bollingerBandElement.Close)}");
}
else if(hedgeOn) // This will check for stop loss violation and close the position
{
Price price = GBPriceCache.GetInstance().GetPrice(hedgeShortSymbol, analysisDate);
if(null == price)
{
MDTrace.WriteLine(LogLevel.DEBUG,$"No price for {hedgeShortSymbol} on {analysisDate.ToShortDateString()}");
continue;
}
if(price.Close<position.TrailingStopLimit)
{
hedgeOn=false;
position.SellDate= analysisDate;
position.CurrentPrice=position.TrailingStopLimit; //bollingerBandElement.Close;
position.Comment=$"Closed to to stop loss {position.Shares} shares @ {Utility.FormatCurrency(position.CurrentPrice)}";
if(position.GainLoss>0)totalWinners++;
else totalLosers++;
totalGainLoss+=position.GainLoss;
cash+=position.MarketValue;
MDTrace.WriteLine(LogLevel.DEBUG,$"Hedge SELL {analysisDate.ToShortDateString()} {Utility.FormatNumber(position.Shares,2)} shares @ {Utility.FormatCurrency(position.CurrentPrice)} Gain/Loss:{Utility.FormatCurrency(position.GainLoss)} Gain/Loss(%):{Utility.FormatPercent(position.GainLossPcnt,2)}");
MDTrace.WriteLine(LogLevel.DEBUG,"**************************************************");
AllPositions.Add(position);
MarketData.Generator.Model.StopLimit stopLimit = new MarketData.Generator.Model.StopLimit()
{
Symbol=hedgeShortSymbol,
AnalysisDate=analysisDate,
PreviousStop=position.PurchasePrice,
NewStop=position.CurrentPrice,
CurrentPriceLow=0.00,
CurrentPriceClose=0.00,
PriceTrendIndicatorSlope=0.00
};
StopLimits.Add(stopLimit);
}
else if(hedgeManager.IsLowerBandBreakIndicator(analysisDate, position))
{
MarketData.Generator.Model.StopLimit stopLimit = hedgeManager.EvaluateStopPriceHedge(analysisDate, position, hedgeStopLimitScalingVolatilityDays, hedgeMinDaysBetweenStopAdjustments);
if(null != stopLimit)StopLimits.Add(stopLimit);
}
}
}
MGSHSessionParams sessionParams = new MGSHSessionParams();
sessionParams.LastUpdated=DateTime.Now;
sessionParams.TradeDate=DateTime.Now;
sessionParams.StartDate=DateTime.Now;
sessionParams.AnalysisDate=DateTime.Now;
sessionParams.Configuration=new MGSHConfiguration();
sessionParams.ActivePositions=new MGSHActivePositions();
sessionParams.AllPositions=AllPositions;
sessionParams.CashBalance=cash;
sessionParams.NonTradeableCash=0.00;
sessionParams.StopLimits=StopLimits;
sessionParams.HedgePositions= new MGSHPositions();
sessionParams.Cycle = 0;
MGSHSessionManager mgSession = new MGSHSessionManager();
mgSession.SaveSession(sessionParams,"MGSH_PROTO_20250128_2.TXT");
MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Total Gain/Loss:{Utility.FormatCurrency(totalGainLoss)}"));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Cash:{Utility.FormatCurrency(cash)}"));
MDTrace.WriteLine(LogLevel.DEBUG,String.Format($"Open:{hedgeOn}"));
return;
}
// ****************************************************************************************************************************************************************
public static void CrossoverExample()
{
// Crossover examples.
// Example 1
//List<String> symbols = PricingDA.GetSymbols();
//List<MovingAverageCrossover> crossovers = new MovingAverageCrossovers();
//DateTime analysisDate = DateTime.Parse("12/31/2024");
//for (int index = 0; index < symbols.Count; index++)
//{
// if (0 == (index % 500)) Console.WriteLine($"Working on {index}");
// String symbol = symbols[index];
// Prices prices = GBPriceCache.GetInstance().GetPrices(symbol, analysisDate, 252);
// MovingAverageCrossovers movingAverageCrossovers = ExponentialMovingAverageCrossover.GetMovingAverageCrossovers(prices, .05); // 5%
// movingAverageCrossovers = new MovingAverageCrossovers(movingAverageCrossovers.Where(x => x.EventDate.Equals(analysisDate)).ToList());
// crossovers.AddRange(movingAverageCrossovers);
//}
//foreach (MovingAverageCrossover movingAverageCrossover in crossovers)
//{
// Console.WriteLine(movingAverageCrossover.ToString());
//}
// Example 2
//String symbol = "SH";
//DateTime analysisDate = DateTime.Parse("12/31/2024");
//Prices prices = GBPriceCache.GetInstance().GetPrices(symbol, analysisDate, 252);
//MovingAverageCrossovers movingAverageCrossovers = ExponentialMovingAverageCrossover.GetMovingAverageCrossovers(prices, .025);
//foreach (MovingAverageCrossover movingAverageCrossover in movingAverageCrossovers)
//{
// Console.WriteLine(movingAverageCrossover.ToString());
//}
//DMAPrices fastDMAPrices = MovingAverageGenerator.GenerateExponentialMovingAverage(prices, 9);
//DMAPrices slowDMAPrices=MovingAverageGenerator.GenerateExponentialMovingAverage(prices, 41);
//DMAPricesByDate fastDMAPricesByDate=fastDMAPrices.GetDMAPricesByDate();
//DMAPricesByDate slowDMAPricesByDate=slowDMAPrices.GetDMAPricesByDate();
//List<DateTime> availableDates = new List<DateTime>(slowDMAPricesByDate.Keys);
//availableDates.Sort((a, b) => DateTime.Compare(a,b)); // earliest date should be in the lowest index, most recent date should be in the highest 2024[0], 2025[1], 2026[2] for example
//StreamWriter streamWriter = new StreamWriter(new FileStream("MovingAverage.csv",FileMode.Create));
//streamWriter.WriteLine("Date,Fast,Slow");
//foreach(DateTime date in availableDates)
//{
// DMAPrice fastDMAPrice = fastDMAPricesByDate[date];
// DMAPrice slowDMAPrice = slowDMAPricesByDate[date];
// streamWriter.WriteLine($"{date.ToShortDateString()},{fastDMAPrice.AVGPrice},{slowDMAPrice.AVGPrice}");
//}
//streamWriter.Flush();
//streamWriter.Close();
//streamWriter.Dispose();
// Example 3
//DateTime beginDate = DateTime.Parse("12/31/2018");
//DateTime stopDate = DateTime.Parse("12/31/2024");
//String symbol="^VIX";
//DateGenerator dateGenerator = new DateGenerator();
//while(beginDate<=stopDate)
//{
// Prices prices = GBPriceCache.GetInstance().GetPrices(symbol, beginDate, 252);
// MovingAverageCrossovers movingAverageCrossovers = ExponentialMovingAverageCrossover.GetMovingAverageCrossovers(prices, .025, 20, 50); // 1.75%
// if(null != movingAverageCrossovers && movingAverageCrossovers.Count>0 && movingAverageCrossovers[0].EventDate.Equals(beginDate))
// {
// MDTrace.WriteLine(LogLevel.DEBUG,$"Found a crossover {movingAverageCrossovers[0].ToString()}");
// }
// beginDate = dateGenerator.FindNextBusinessDay(beginDate);
//}
}
@@ -771,19 +982,40 @@ namespace MarketData
Trace.Listeners.Add(new TextWriterTraceListener(strLogFile));
DateTime currentDate=DateTime.Now;
Prices prices = new Prices();
prices.Add(new Price(){Date=DateTime.Parse("1/28/2025"),Open=10,High=10,Low=10,Close=10});
prices.Add(new Price(){Date=DateTime.Parse("1/29/2025"),Open=15,High=15,Low=15,Close=15});
prices.Add(new Price(){Date=DateTime.Parse("1/30/2025"),Open=5,High=5,Low=5,Close=5});
prices.Add(new Price(){Date=DateTime.Parse("1/31/2025"),Open=20,High=20,Low=20,Close=20});
prices.Add(new Price(){Date=DateTime.Parse("2/1/2025"),Open=10,High=10,Low=10,Close=10});
prices.Add(new Price(){Date=DateTime.Parse("2/2/2025"),Open=25,High=25,Low=25,Close=25});
prices.Add(new Price(){Date=DateTime.Parse("2/3/2025"),Open=30,High=30,Low=30,Close=30});
DMAPrices dmaPrices = MovingAverageGenerator.GenerateMovingAverage(prices, 2);
DMAPrices emaPrices = MovingAverageGenerator.GenerateExponentialMovingAverage(prices, 2);
// CompanyProfile c1 = MarketDataHelper.GetCompanyProfile("AAME");
CompanyProfiles companyProfiles = CompanyProfileDA.GetCompanyProfiles();
List<String> symbols = companyProfiles.Select(x => x.Symbol).ToList();
foreach(String symbol in symbols)
{
CompanyProfile companyProfile = MarketDataHelper.GetCompanyProfile(symbol);
if (null != companyProfile) {
MDTrace.WriteLine(LogLevel.DEBUG, $"Symbol:{symbol} Sector:{companyProfile.Sector} Industry:{companyProfile.Industry} Description:{companyProfile.Description}");
MDTrace.WriteLine(LogLevel.DEBUG, "");
}
Thread.Sleep(2000);
}
// CompanyProfile companyProfile = MarketDataHelper.GetCompanyProfile("MOD");
//PortfolioTrades portfolioTrades = PortfolioDA.GetOpenTrades();
//List<String> symbols = portfolioTrades.Select(x => x.Symbol).Distinct().ToList();
//for (int rindex=0; rindex<symbols.Count; rindex++)
//{
// String symbol = symbols[rindex];
// CompanyProfile companyProfile = MarketDataHelper.GetCompanyProfile(symbol);
// if (null != companyProfile)
// {
// MDTrace.WriteLine(LogLevel.DEBUG, $"Symbol:{symbol} Sector:{companyProfile.Sector} Industry:{companyProfile.Industry} Description:{companyProfile.Description}");
// MDTrace.WriteLine(LogLevel.DEBUG,"");
// }
// Thread.Sleep(1000);
//}
DateTime maxHolidayDate =HolidayDA.GetMaxHolidayDate();
if(currentDate>maxHolidayDate)
@@ -1461,7 +1693,22 @@ namespace MarketData
Program.HandleCMTSession(args);
}
// *************************************************************************************************************************************************************************************************************************
// ************************************************************************************************** M G M O M E N T U M *************************************************************************************************
// ************************************************************************************************** M G S H M O M E N T U M *************************************************************************************************
// *************************************************************************************************************************************************************************************************************************
else if(arg.Equals("MGSHSESSION"))
{
MGSHMomentumHelper.HandleMGSHSession(args);
}
else if(arg.Equals("MGSHRUNBACKTEST"))
{
MGSHMomentumHelper.HandleMGSHRunBacktest(args);
}
else if(arg.Equals("MGSHRUNDAILY"))
{
MGSHMomentumHelper.HandleMGSHRunDaily(args);
}
// *************************************************************************************************************************************************************************************************************************
// ************************************************************************************************** M G M O M E N T U M *************************************************************************************************
// *************************************************************************************************************************************************************************************************************************
else if (arg.Equals("RUNMOMENTUM"))
{
@@ -2011,7 +2258,7 @@ namespace MarketData
ThreadPool.QueueUserWorkItem(delegate
{
UpdateAnalystPriceTarget(); // finance.yahoo.com
UpdateAnalystPriceTarget(); // MarketBeat
GetETFHoldings(); // finance.yahoo.com
resetEvents[STAGE_9].Set();
});