Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0b2350812c | |||
| 67aa1bd0cf | |||
| 2f9c01e5a0 | |||
| d0eb2f38f6 | |||
| 860167b3d3 |
508
MarketDataLib/Cache/GLPriceCache.cs
Normal file
508
MarketDataLib/Cache/GLPriceCache.cs
Normal file
@@ -0,0 +1,508 @@
|
||||
using MarketData.MarketDataModel;
|
||||
using MarketData.Utils;
|
||||
using MarketData.DataAccess;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MarketData.Cache
|
||||
{
|
||||
public class GLPriceCache : IDisposable
|
||||
{
|
||||
// ── Singleton ────────────────────────────────────────────────────────────
|
||||
private static readonly object instanceLock = new object();
|
||||
private static GLPriceCache instance = null;
|
||||
|
||||
public static GLPriceCache GetInstance()
|
||||
{
|
||||
lock (instanceLock)
|
||||
{
|
||||
if (instance == null)
|
||||
instance = new GLPriceCache();
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
// ── State ────────────────────────────────────────────────────────────────
|
||||
private readonly Dictionary<string, PricesByDate> priceCache = new Dictionary<string, PricesByDate>();
|
||||
private readonly ConcurrentDictionary<string, object> symbolFetchLocks = new ConcurrentDictionary<string, object>();
|
||||
private readonly object cacheLock = new object();
|
||||
private DateTime latestDate = Utility.Epoch;
|
||||
|
||||
// ── Background refresh ───────────────────────────────────────────────────
|
||||
private readonly TimeSpan cacheCycle = TimeSpan.FromMinutes(5);
|
||||
private Timer refreshTimer = null;
|
||||
private int tickCount = 0;
|
||||
private const int evictionTickInterval = 12; // every 12 ticks x 5 min = 1 hour
|
||||
|
||||
// ── Parallelism ──────────────────────────────────────────────────────────
|
||||
private static readonly int maxParallelDbCalls = ResolveMaxParallelDbCalls();
|
||||
|
||||
private static int ResolveMaxParallelDbCalls()
|
||||
{
|
||||
int @default = Math.Min(Math.Max(1, Environment.ProcessorCount) * 3, 32);
|
||||
string configured = Environment.GetEnvironmentVariable("GL_PRICE_CACHE_PARALLEL_DB_CALLS");
|
||||
return int.TryParse(configured, out int parsed) && parsed > 0 ? parsed : @default;
|
||||
}
|
||||
|
||||
// ── Disposal ─────────────────────────────────────────────────────────────
|
||||
private volatile bool disposed = false;
|
||||
private int refreshInProgress = 0;
|
||||
|
||||
// ── Constructor ──────────────────────────────────────────────────────────
|
||||
private GLPriceCache()
|
||||
{
|
||||
refreshTimer = new Timer(OnCacheRefreshTick, null, cacheCycle, cacheCycle);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed) return;
|
||||
disposed = true;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
Timer timerToDispose;
|
||||
lock (instanceLock)
|
||||
{
|
||||
timerToDispose = refreshTimer;
|
||||
refreshTimer = null;
|
||||
instance = null;
|
||||
}
|
||||
|
||||
// Block until any in-flight tick completes before disposing
|
||||
using (var waited = new ManualResetEventSlim(false))
|
||||
{
|
||||
if (timerToDispose != null)
|
||||
timerToDispose.Dispose(waited.WaitHandle);
|
||||
waited.Wait(TimeSpan.FromSeconds(10));
|
||||
}
|
||||
|
||||
MDTrace.WriteLine(LogLevel.DEBUG, "[GLPriceCache:Dispose] Disposed.");
|
||||
}
|
||||
}
|
||||
|
||||
// ── Background tick ──────────────────────────────────────────────────────
|
||||
private void OnCacheRefreshTick(object state)
|
||||
{
|
||||
if (disposed) return;
|
||||
try
|
||||
{
|
||||
lock (cacheLock)
|
||||
{
|
||||
long count = CountInternal();
|
||||
MDTrace.WriteLine(LogLevel.DEBUG,
|
||||
$"[GLPriceCache:Tick] Symbols: {priceCache.Keys.Count}. " +
|
||||
$"Items in cache: {Utility.FormatNumber(count, 0, true)}.");
|
||||
}
|
||||
|
||||
if (++tickCount % evictionTickInterval == 0)
|
||||
EvictStaleSymbols();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MDTrace.WriteLine(LogLevel.DEBUG,
|
||||
$"[GLPriceCache:Tick] [ERROR] Unhandled exception: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
// ── Eviction ─────────────────────────────────────────────────────────────
|
||||
private void EvictStaleSymbols()
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref refreshInProgress, 1, 0) == 1)
|
||||
{
|
||||
MDTrace.WriteLine(LogLevel.DEBUG, "[GLPriceCache:Evict] Skipped — refresh in progress.");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
int count;
|
||||
lock (cacheLock)
|
||||
{
|
||||
count = priceCache.Count;
|
||||
priceCache.Clear();
|
||||
symbolFetchLocks.Clear();
|
||||
}
|
||||
|
||||
MDTrace.WriteLine(LogLevel.DEBUG,
|
||||
$"[GLPriceCache:Evict] Cache cleared. {count} symbols evicted.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Exchange(ref refreshInProgress, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Public API ───────────────────────────────────────────────────────────
|
||||
|
||||
public void Add(PortfolioTrades portfolioTrades)
|
||||
{
|
||||
List<string> symbols = portfolioTrades.Symbols;
|
||||
|
||||
Dictionary<string, DateTime> minTradeDates = symbols.ToDictionary(
|
||||
sym => sym, sym => portfolioTrades.GetMinTradeDate(sym));
|
||||
|
||||
// Only open positions need an intraday price refresh.
|
||||
// Closed positions, regardless of close date, have immutable prices — skip the DB call.
|
||||
HashSet<string> mutableSymbols = new HashSet<string>(
|
||||
symbols.Where(sym => portfolioTrades.HasOpenPositions(sym)));
|
||||
|
||||
Dictionary<string, DateTime> minCacheDates;
|
||||
lock (cacheLock)
|
||||
{
|
||||
minCacheDates = symbols.ToDictionary(
|
||||
sym => sym,
|
||||
sym => priceCache.ContainsKey(sym) ? priceCache[sym].MinDate : DateTime.MaxValue);
|
||||
}
|
||||
|
||||
ConcurrentDictionary<string, Prices> fetchedPrices = new ConcurrentDictionary<string, Prices>();
|
||||
ConcurrentDictionary<string, Price> latestPrices = new ConcurrentDictionary<string, Price>();
|
||||
|
||||
Parallel.ForEach(symbols, new ParallelOptions { MaxDegreeOfParallelism = maxParallelDbCalls }, symbol =>
|
||||
{
|
||||
if (disposed) return;
|
||||
|
||||
DateTime minTradeDate = minTradeDates[symbol];
|
||||
DateTime minCacheDate = minCacheDates[symbol];
|
||||
|
||||
// Acquire a per-symbol lock to prevent concurrent clients from issuing
|
||||
// duplicate DB fetches for the same symbol. First thread fetches;
|
||||
// subsequent threads for the same symbol block here, then re-check
|
||||
// the cache on entry and skip the fetch if already populated.
|
||||
object symbolLock = symbolFetchLocks.GetOrAdd(symbol, _ => new object());
|
||||
lock (symbolLock)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Re-check cache state after acquiring symbol lock — another
|
||||
// client thread may have already fetched this symbol while we waited
|
||||
lock (cacheLock)
|
||||
{
|
||||
if (priceCache.ContainsKey(symbol))
|
||||
minCacheDate = priceCache[symbol].MinDate;
|
||||
}
|
||||
|
||||
// Historical fetch — only when cache is missing or incomplete
|
||||
Prices prices = null;
|
||||
if (minCacheDate == DateTime.MaxValue)
|
||||
prices = PricingDA.GetPrices(symbol, minTradeDate);
|
||||
else if (minTradeDate < minCacheDate)
|
||||
prices = PricingDA.GetPrices(symbol, minCacheDate, minTradeDate);
|
||||
|
||||
if (prices != null && prices.Count > 0)
|
||||
fetchedPrices[symbol] = prices;
|
||||
|
||||
// Intraday refresh — mutable symbols only
|
||||
if (mutableSymbols.Contains(symbol))
|
||||
{
|
||||
Price latestPrice = PricingDA.GetPrice(symbol);
|
||||
if (latestPrice != null)
|
||||
latestPrices[symbol] = latestPrice;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MDTrace.WriteLine(LogLevel.DEBUG,
|
||||
$"[GLPriceCache:Add] [ERROR] Failed fetching prices for {symbol}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
lock (cacheLock)
|
||||
{
|
||||
// Historical prices — idempotent, will not overwrite existing entries
|
||||
foreach (KeyValuePair<string, Prices> kvp in fetchedPrices)
|
||||
foreach (Price price in kvp.Value)
|
||||
AddInternal(price);
|
||||
|
||||
// Latest prices — unconditional overwrite to capture intraday updates
|
||||
foreach (KeyValuePair<string, Price> kvp in latestPrices)
|
||||
{
|
||||
PricesByDate pricesByDate;
|
||||
if (!priceCache.TryGetValue(kvp.Key, out pricesByDate))
|
||||
{
|
||||
pricesByDate = new PricesByDate();
|
||||
priceCache[kvp.Key] = pricesByDate;
|
||||
}
|
||||
|
||||
if (pricesByDate.ContainsKey(kvp.Value.Date))
|
||||
pricesByDate.Remove(kvp.Value.Date);
|
||||
pricesByDate.Add(kvp.Value.Date, kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
MDTrace.WriteLine(LogLevel.DEBUG,
|
||||
$"[GLPriceCache:Add] Symbols: {symbols.Count}, Mutable: {mutableSymbols.Count}, " +
|
||||
$"Historical fetches: {fetchedPrices.Count}, Intraday updates: {latestPrices.Count}");
|
||||
}
|
||||
|
||||
public void Add(Prices prices)
|
||||
{
|
||||
foreach (Price price in prices)
|
||||
Add(price);
|
||||
}
|
||||
|
||||
public void Add(List<string> symbols, DateTime pricingDate)
|
||||
{
|
||||
if (symbols == null || symbols.Count == 0) return;
|
||||
|
||||
ConcurrentDictionary<string, Price> fetchedPrices = new ConcurrentDictionary<string, Price>();
|
||||
|
||||
Parallel.ForEach(symbols, new ParallelOptions { MaxDegreeOfParallelism = maxParallelDbCalls }, symbol =>
|
||||
{
|
||||
if (disposed) return;
|
||||
|
||||
lock (cacheLock)
|
||||
{
|
||||
if (ContainsPriceInternal(symbol, pricingDate)) return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Price price = PricingDA.GetPrice(symbol, pricingDate);
|
||||
if (price != null) fetchedPrices[symbol] = price;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MDTrace.WriteLine(LogLevel.DEBUG,
|
||||
$"[GLPriceCache:Add] [ERROR] Failed fetching price for {symbol} on {pricingDate:yyyy-MM-dd}: {ex.Message}");
|
||||
}
|
||||
});
|
||||
|
||||
lock (cacheLock)
|
||||
{
|
||||
foreach (KeyValuePair<string, Price> kvp in fetchedPrices)
|
||||
AddInternal(kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(Price price)
|
||||
{
|
||||
lock (cacheLock)
|
||||
{
|
||||
AddInternal(price);
|
||||
}
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref refreshInProgress, 1, 0) == 1)
|
||||
{
|
||||
MDTrace.WriteLine(LogLevel.DEBUG, "[GLPriceCache:Refresh] Skipped — refresh already in progress.");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
List<string> symbols;
|
||||
Dictionary<string, DateTime> currentMaxDates;
|
||||
|
||||
lock (cacheLock)
|
||||
{
|
||||
symbols = priceCache.Keys.ToList();
|
||||
currentMaxDates = priceCache.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.MaxDate);
|
||||
}
|
||||
|
||||
if (symbols.Count == 0) return;
|
||||
|
||||
ConcurrentDictionary<string, PricesByDate> fullReloads = new ConcurrentDictionary<string, PricesByDate>();
|
||||
ConcurrentDictionary<string, Price> singleUpdates = new ConcurrentDictionary<string, Price>();
|
||||
|
||||
// Fetch outside the cache lock — no timeout risk holding cacheLock over I/O
|
||||
Dictionary<string, DateTime> maxDbDates = PricingDA.GetLatestDates(symbols);
|
||||
DateTime latestDateFromDb = PricingDA.GetLatestDate();
|
||||
|
||||
Parallel.ForEach(symbols, new ParallelOptions { MaxDegreeOfParallelism = maxParallelDbCalls }, symbol =>
|
||||
{
|
||||
if (disposed) return;
|
||||
|
||||
DateTime cachedMax;
|
||||
if (!currentMaxDates.TryGetValue(symbol, out cachedMax)) return;
|
||||
|
||||
try
|
||||
{
|
||||
DateTime dbMax;
|
||||
if (maxDbDates.TryGetValue(symbol, out dbMax) && dbMax.Date != cachedMax.Date)
|
||||
{
|
||||
Prices prices = PricingDA.GetPrices(symbol, cachedMax);
|
||||
if (prices != null) fullReloads[symbol] = prices.GetPricesByDate();
|
||||
}
|
||||
else
|
||||
{
|
||||
Price price = PricingDA.GetPrice(symbol, cachedMax);
|
||||
if (price != null) singleUpdates[symbol] = price;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MDTrace.WriteLine(LogLevel.DEBUG,
|
||||
$"[GLPriceCache:Refresh] [ERROR] Failed refreshing {symbol}: {ex.Message}");
|
||||
}
|
||||
});
|
||||
|
||||
lock (cacheLock)
|
||||
{
|
||||
latestDate = latestDateFromDb;
|
||||
|
||||
foreach (KeyValuePair<string, PricesByDate> kvp in fullReloads)
|
||||
{
|
||||
PricesByDate existing;
|
||||
if (priceCache.TryGetValue(kvp.Key, out existing) &&
|
||||
existing.MaxDate == currentMaxDates[kvp.Key])
|
||||
{
|
||||
priceCache[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, Price> kvp in singleUpdates)
|
||||
{
|
||||
PricesByDate pricesByDate;
|
||||
if (priceCache.TryGetValue(kvp.Key, out pricesByDate) &&
|
||||
pricesByDate.MaxDate == currentMaxDates[kvp.Key])
|
||||
{
|
||||
if (pricesByDate.ContainsKey(kvp.Value.Date))
|
||||
pricesByDate.Remove(kvp.Value.Date);
|
||||
pricesByDate.Add(kvp.Value.Date, kvp.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MDTrace.WriteLine(LogLevel.DEBUG,
|
||||
$"[GLPriceCache:Refresh] Full reloads: {fullReloads.Count}, Single updates: {singleUpdates.Count}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Exchange(ref refreshInProgress, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime GetLatestDate()
|
||||
{
|
||||
lock (cacheLock)
|
||||
{
|
||||
if (Utility.IsEpoch(latestDate))
|
||||
latestDate = PricingDA.GetLatestDate();
|
||||
return latestDate;
|
||||
}
|
||||
}
|
||||
|
||||
public void RefreshLatestDate()
|
||||
{
|
||||
lock (cacheLock)
|
||||
{
|
||||
latestDate = PricingDA.GetLatestDate();
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime GetMinCacheDate(string symbol)
|
||||
{
|
||||
lock (cacheLock)
|
||||
{
|
||||
PricesByDate symbolPrices;
|
||||
if (!priceCache.TryGetValue(symbol, out symbolPrices) || symbolPrices.Count == 0)
|
||||
return Utility.Epoch;
|
||||
return symbolPrices.MinDate;
|
||||
}
|
||||
}
|
||||
|
||||
public Prices GetPrices(string symbol, DateTime endDate, int dayCount)
|
||||
{
|
||||
lock (cacheLock)
|
||||
{
|
||||
PricesByDate pricesByDate;
|
||||
if (!priceCache.TryGetValue(symbol, out pricesByDate)) return new Prices();
|
||||
|
||||
DateGenerator dateGenerator = new DateGenerator();
|
||||
List<DateTime> historicalDates = dateGenerator.GenerateHistoricalDates(endDate, dayCount);
|
||||
|
||||
Prices result = new Prices();
|
||||
foreach (DateTime date in historicalDates)
|
||||
if (pricesByDate.ContainsKey(date))
|
||||
result.Add(pricesByDate[date]);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public Price GetPrice(string symbol, DateTime date)
|
||||
{
|
||||
lock (cacheLock)
|
||||
{
|
||||
PricesByDate pricesByDate;
|
||||
if (!priceCache.TryGetValue(symbol, out pricesByDate)) return null;
|
||||
Price price;
|
||||
return pricesByDate.TryGetValue(date, out price) ? price : null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ContainsPrice(string symbol, DateTime date)
|
||||
{
|
||||
lock (cacheLock)
|
||||
{
|
||||
return ContainsPriceInternal(symbol, date);
|
||||
}
|
||||
}
|
||||
|
||||
public bool ContainsPrice(List<string> symbols, DateTime date)
|
||||
{
|
||||
if (symbols == null || symbols.Count == 0) return false;
|
||||
lock (cacheLock)
|
||||
{
|
||||
foreach (string symbol in symbols)
|
||||
if (!ContainsPriceInternal(symbol, date)) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ContainsSymbol(string symbol)
|
||||
{
|
||||
lock (cacheLock)
|
||||
{
|
||||
return priceCache.ContainsKey(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Private helpers ──────────────────────────────────────────────────────
|
||||
|
||||
// Must be called under cacheLock
|
||||
private void AddInternal(Price price)
|
||||
{
|
||||
if (price == null) return;
|
||||
PricesByDate pricesByDate;
|
||||
if (!priceCache.TryGetValue(price.Symbol, out pricesByDate))
|
||||
{
|
||||
pricesByDate = new PricesByDate();
|
||||
priceCache[price.Symbol] = pricesByDate;
|
||||
}
|
||||
if (!pricesByDate.ContainsKey(price.Date))
|
||||
pricesByDate.Add(price.Date, price);
|
||||
}
|
||||
|
||||
// Must be called under cacheLock
|
||||
private bool ContainsPriceInternal(string symbol, DateTime date)
|
||||
{
|
||||
PricesByDate pricesByDate;
|
||||
if (!priceCache.TryGetValue(symbol, out pricesByDate)) return false;
|
||||
return pricesByDate.ContainsKey(date);
|
||||
}
|
||||
|
||||
// Must be called under cacheLock
|
||||
private long CountInternal()
|
||||
{
|
||||
long count = 0;
|
||||
foreach (PricesByDate pricesByDate in priceCache.Values)
|
||||
count += pricesByDate.Count;
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ namespace MarketData.Generator.GainLoss
|
||||
}
|
||||
//public void RefreshPriceCache()
|
||||
//{
|
||||
// LocalPriceCache.GetInstance().Refresh();
|
||||
// GLPriceCache.GetInstance().Refresh();
|
||||
//}
|
||||
// *****************************************************************************************************************************************************************
|
||||
// ************************************************ G E N E R A T E A C T I V E G A I N L O S S / G A I N L O S S P E R C E N T *****************************
|
||||
@@ -26,7 +26,7 @@ namespace MarketData.Generator.GainLoss
|
||||
public GainLossCollection GenerateGainLoss(PortfolioTrades portfolioTrades,DateTime? maxDateRef=null)
|
||||
{
|
||||
if (null == portfolioTrades || 0 == portfolioTrades.Count) return null;
|
||||
LocalPriceCache.GetInstance().Add(portfolioTrades);
|
||||
GLPriceCache.GetInstance().Add(portfolioTrades);
|
||||
DateTime minTradeDate = portfolioTrades.GetMinTradeDate();
|
||||
DateTime maxDate = PricingDA.GetLatestDate();
|
||||
if(null!=maxDateRef)maxDate=maxDateRef.Value;
|
||||
@@ -46,11 +46,11 @@ namespace MarketData.Generator.GainLoss
|
||||
gainLoss.Add(holdingDate, new GainLossItem(holdingDate, 0,0,false));
|
||||
continue;
|
||||
}
|
||||
if(!LocalPriceCache.GetInstance().ContainsPrice(openTrades.Symbols,holdingDate))
|
||||
if(!GLPriceCache.GetInstance().ContainsPrice(openTrades.Symbols,holdingDate))
|
||||
{
|
||||
if(holdingDate.Date.Equals(maxDate))
|
||||
{
|
||||
LocalPriceCache.GetInstance().Add(openTrades.Symbols,holdingDate);
|
||||
GLPriceCache.GetInstance().Add(openTrades.Symbols,holdingDate);
|
||||
}else continue;
|
||||
}
|
||||
foreach (PortfolioTrade portfolioTrade in openTrades)
|
||||
|
||||
@@ -22,10 +22,10 @@ namespace MarketData.Generator.GainLoss
|
||||
public TotalGainLossCollection GenerateTotalGainLoss(PortfolioTrades portfolioTrades,DateTime? maxDateRef=null)
|
||||
{
|
||||
if (null == portfolioTrades || 0 == portfolioTrades.Count) return null;
|
||||
LocalPriceCache.GetInstance().Add(portfolioTrades);
|
||||
GLPriceCache.GetInstance().Add(portfolioTrades);
|
||||
DateTime minTradeDate = portfolioTrades.GetMinTradeDate();
|
||||
// DateTime maxDate = PricingDA.GetLatestDate();
|
||||
DateTime maxDate=LocalPriceCache.GetInstance().GetLatestDate();
|
||||
DateTime maxDate=GLPriceCache.GetInstance().GetLatestDate();
|
||||
if(null!=maxDateRef)maxDate=maxDateRef.Value;
|
||||
Dictionary<DateTime,TotalGainLossItem> gainLossCollection = new Dictionary<DateTime, TotalGainLossItem>();
|
||||
DateGenerator dateGenerator = new DateGenerator();
|
||||
@@ -75,10 +75,10 @@ namespace MarketData.Generator.GainLoss
|
||||
public TotalGainLossCollection GenerateTotalGainLossWithDividends(PortfolioTrades portfolioTrades,DividendPayments dividendPayments,DateTime? maxDateRef=null)
|
||||
{
|
||||
if (null == portfolioTrades || 0 == portfolioTrades.Count) return null;
|
||||
LocalPriceCache.GetInstance().Add(portfolioTrades);
|
||||
GLPriceCache.GetInstance().Add(portfolioTrades);
|
||||
DateTime minTradeDate = portfolioTrades.GetMinTradeDate();
|
||||
// DateTime maxDate = PricingDA.GetLatestDate();
|
||||
DateTime maxDate=LocalPriceCache.GetInstance().GetLatestDate();
|
||||
DateTime maxDate=GLPriceCache.GetInstance().GetLatestDate();
|
||||
if(null!=maxDateRef)maxDate=maxDateRef.Value;
|
||||
Dictionary<DateTime,TotalGainLossItem> gainLossCollection = new Dictionary<DateTime, TotalGainLossItem>();
|
||||
DateGenerator dateGenerator = new DateGenerator();
|
||||
|
||||
@@ -19,12 +19,12 @@ namespace MarketData.Generator.GainLoss
|
||||
DateGenerator dateGenerator=new DateGenerator();
|
||||
ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries();
|
||||
List<TotalGainLossItem> gainLossList=new List<TotalGainLossItem>();
|
||||
LocalPriceCache.GetInstance().Add(portfolioTrades);
|
||||
GLPriceCache.GetInstance().Add(portfolioTrades);
|
||||
try
|
||||
{
|
||||
if(!ValidatePortfolioTrades(portfolioTrades))return null;
|
||||
DateTime minDate=portfolioTrades.GetMinTradeDate();
|
||||
DateTime maxDate = LocalPriceCache.GetInstance().GetLatestDate();
|
||||
DateTime maxDate = GLPriceCache.GetInstance().GetLatestDate();
|
||||
if(null!=maxDateRef) maxDate=maxDateRef.Value;
|
||||
double prevGainLoss=double.NaN;
|
||||
List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(minDate,maxDate);
|
||||
@@ -46,7 +46,7 @@ namespace MarketData.Generator.GainLoss
|
||||
foreach(PortfolioTrade openPosition in openPositions)
|
||||
{
|
||||
exposure+=openPosition.Shares*openPosition.Price;
|
||||
Price price=LocalPriceCache.GetInstance().GetPrice(openPosition.Symbol,currentDate);
|
||||
Price price=GLPriceCache.GetInstance().GetPrice(openPosition.Symbol,currentDate);
|
||||
if(null==price)
|
||||
{
|
||||
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString()));
|
||||
@@ -98,12 +98,12 @@ namespace MarketData.Generator.GainLoss
|
||||
DateGenerator dateGenerator=new DateGenerator();
|
||||
ModelPerformanceSeries performanceSeries=new ModelPerformanceSeries();
|
||||
List<TotalGainLossItem> gainLossList=new List<TotalGainLossItem>();
|
||||
LocalPriceCache.GetInstance().Add(portfolioTrades);
|
||||
GLPriceCache.GetInstance().Add(portfolioTrades);
|
||||
try
|
||||
{
|
||||
if(!ValidatePortfolioTrades(portfolioTrades)) return null;
|
||||
DateTime minDate=portfolioTrades.Min(x => x.TradeDate);
|
||||
DateTime maxDate = LocalPriceCache.GetInstance().GetLatestDate();
|
||||
DateTime maxDate = GLPriceCache.GetInstance().GetLatestDate();
|
||||
double prevGainLoss=double.NaN;
|
||||
List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(minDate,maxDate);
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace MarketData.Generator.GainLoss
|
||||
foreach(PortfolioTrade openPosition in openPositions)
|
||||
{
|
||||
exposure+=openPosition.Shares*openPosition.Price;
|
||||
Price price=LocalPriceCache.GetInstance().GetPrice(openPosition.Symbol,currentDate);
|
||||
Price price=GLPriceCache.GetInstance().GetPrice(openPosition.Symbol,currentDate);
|
||||
if(null==price)
|
||||
{
|
||||
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString()));
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace MarketData.Generator.GainLoss
|
||||
if(holdingDate<portfolioTrade.TradeDate) return null;
|
||||
if(portfolioTrade.IsOpen||(portfolioTrade.IsClosed&&portfolioTrade.SellDate>holdingDate))
|
||||
{
|
||||
Price price=LocalPriceCache.GetInstance().GetPrice(portfolioTrade.Symbol,holdingDate);
|
||||
Price price=GLPriceCache.GetInstance().GetPrice(portfolioTrade.Symbol,holdingDate);
|
||||
if(null==price)
|
||||
{
|
||||
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No price for {0} on {1}",portfolioTrade.Symbol,Utility.DateTimeToStringMMHDDHYYYY(holdingDate)));
|
||||
@@ -35,7 +35,7 @@ namespace MarketData.Generator.GainLoss
|
||||
if(holdingDate<portfolioTrade.TradeDate) return null;
|
||||
if(portfolioTrade.IsOpen||(portfolioTrade.IsClosed&&portfolioTrade.SellDate>holdingDate))
|
||||
{
|
||||
Price price=LocalPriceCache.GetInstance().GetPrice(portfolioTrade.Symbol,holdingDate);
|
||||
Price price=GLPriceCache.GetInstance().GetPrice(portfolioTrade.Symbol,holdingDate);
|
||||
if(null==price)
|
||||
{
|
||||
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No price for {0} on {1}",portfolioTrade.Symbol,Utility.DateTimeToStringMMHDDHYYYY(holdingDate)));
|
||||
@@ -69,7 +69,7 @@ namespace MarketData.Generator.GainLoss
|
||||
{
|
||||
return (portfolioTrade.SellPrice*portfolioTrade.Shares)-(portfolioTrade.Price*portfolioTrade.Shares);
|
||||
}
|
||||
Price price=LocalPriceCache.GetInstance().GetPrice(portfolioTrade.Symbol,holdingDate);
|
||||
Price price=GLPriceCache.GetInstance().GetPrice(portfolioTrade.Symbol,holdingDate);
|
||||
if(null==price)
|
||||
{
|
||||
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No price for {0} on {1}",portfolioTrade.Symbol,Utility.DateTimeToStringMMHDDHYYYY(holdingDate)));
|
||||
@@ -83,7 +83,7 @@ namespace MarketData.Generator.GainLoss
|
||||
if(!portfolioTrade.SellDate.Equals(Utility.Epoch)&&holdingDate>portfolioTrade.SellDate) return null;
|
||||
// check to see if we bought and sold on the same date.
|
||||
if(portfolioTrade.SellDate.Equals(portfolioTrade.TradeDate)) return (portfolioTrade.SellPrice*portfolioTrade.Shares);
|
||||
Price price=LocalPriceCache.GetInstance().GetPrice(portfolioTrade.Symbol,holdingDate);
|
||||
Price price=GLPriceCache.GetInstance().GetPrice(portfolioTrade.Symbol,holdingDate);
|
||||
if(null==price)
|
||||
{
|
||||
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No price for {0} on {1}",portfolioTrade.Symbol,Utility.DateTimeToStringMMHDDHYYYY(holdingDate)));
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="Cache\DividendHistoryCache.cs" />
|
||||
<Compile Include="Cache\GBPriceCache.cs" />
|
||||
<Compile Include="Cache\GLPriceCache.cs" />
|
||||
<Compile Include="Cache\LocalPriceCache.cs" />
|
||||
<Compile Include="CNNProcessing\BitmapExtensions.cs" />
|
||||
<Compile Include="CNNProcessing\CNNClient.cs" />
|
||||
|
||||
@@ -1857,6 +1857,7 @@ namespace MarketData
|
||||
{
|
||||
LocalPriceCache.GetInstance().Dispose();
|
||||
GBPriceCache.GetInstance().Dispose();
|
||||
GLPriceCache.GetInstance().Dispose();
|
||||
}
|
||||
} // main
|
||||
// *****************************************************************************************************************************************************************************
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
CMTSESSIONv1.00
|
||||
LastUpdated=2/23/2026 9:38:02 PM
|
||||
TradeDate=2/23/2026
|
||||
LastUpdated=2/25/2026 9:12:01 PM
|
||||
TradeDate=2/25/2026
|
||||
StartDate=1/1/0001
|
||||
AnalysisDate=2/23/2026
|
||||
AnalysisDate=2/25/2026
|
||||
CashBalance=4287.32
|
||||
NonTradeableCash=6456.42
|
||||
SuspendTrading=False|UsePriceSlopeIndicator=True|UsePriceSlopeIndicatorDays=252|AnalysisDate=2/23/2026|BetaMonths=6|TradeDate=2/23/2026|MarketCapLowerLimit=500000000|SidewaysDetection=False|SidewaysAfterDays=30|PriceTrendDays=20|CheckOutliersInReturnStream=True|DailyReturnLimit=0.25|MaxDailyPositions=3|MaxOpenPositions=3|NoTradeSymbols=CODYY,MARUY,CSTM,CS,NATI,QADA,CRTO,GTBIF,CLCT,PRSC,CMD,STAY,GBTC,YOKU,PNY,RFMD,ASAZY,USMO,VNR,STB,XIV,SYNT,DFP|OnlyTradeSymbols=|MinRSI=70|InitialCash=10000|TotalRiskPercentDecimal=0.05|PositionRiskPercentDecimal=0.12|EquityOnly=False|MinPercentReturnProximityTo52WeekHigh=30|MinPercentReturnOver52WeekLow=80|ProfitMarginCheck=True|EPSCheck=True|MinDaysBetweenReholding=30|LiquidityCheck=True|MinVolume=1000|DMA200Horizon=15|MinDaysBetweenStopAdjustments=30|MinDaysBetweenInitialStopAdjustment=5|MaxPricingExceptions=3|MACDSetup=(12,26,9)|MACDSignalDays=5|MACDRejectStrongSells=True|MACDRejectWeakSells=True|UseMarketIndicator=True|Benchmark=SPY|BenchmarkMovingAverageDays=200|BenchmarkMovingAverageHorizon=5|UseMarketIndicatorVolatility=True|UseMarketIndicatorVolatilityHorizon=60|UseMarketIndicatorVolatilityBenchmark=^VIX|UseStopLimitScaling=True|StopLimitScalingType=AverageTrueRange|StopLimitScalingVolatilityDays=30|SellOnDMABreak=True|DMABreakValues=200|DMABreakForceBreak=False|EntryType=OverExtended,MVP,PriceTrend,VolumeTrend|EntryHorizon=30|CandidateExpiryDays=180|VolumeTrendDays=10|ChannelBreakoutHorizon=40|UseOverExtendedIndicatorDays=45|UseOverExtendedIndicatorViolationThreshhold=1|UseOverExtendedIndicatorMarginPercent=1|MaxBeta=10|UseMaxBeta=False|UseProfitMaximization=True|UseProfitMaximizationExpression=R_THRESSHOLD=4;MAX_ATR=3;MULTIPLIER=MAX_ATR;IF(RMultiple>=R_THRESSHOLD){MULTIPLIER=1.2;}|UseTradeOnlySectors=False|UseTradeOnlySectorsSectors=Healthcare,Technology,Basic Materials,Consumer Defensive,Industrials|EvaluateStopOnUpTrend=False
|
||||
SuspendTrading=False|UsePriceSlopeIndicator=True|UsePriceSlopeIndicatorDays=252|AnalysisDate=2/25/2026|BetaMonths=6|TradeDate=2/25/2026|MarketCapLowerLimit=500000000|SidewaysDetection=False|SidewaysAfterDays=30|PriceTrendDays=20|CheckOutliersInReturnStream=True|DailyReturnLimit=0.25|MaxDailyPositions=3|MaxOpenPositions=3|NoTradeSymbols=CODYY,MARUY,CSTM,CS,NATI,QADA,CRTO,GTBIF,CLCT,PRSC,CMD,STAY,GBTC,YOKU,PNY,RFMD,ASAZY,USMO,VNR,STB,XIV,SYNT,DFP|OnlyTradeSymbols=|MinRSI=70|InitialCash=10000|TotalRiskPercentDecimal=0.05|PositionRiskPercentDecimal=0.12|EquityOnly=False|MinPercentReturnProximityTo52WeekHigh=30|MinPercentReturnOver52WeekLow=80|ProfitMarginCheck=True|EPSCheck=True|MinDaysBetweenReholding=30|LiquidityCheck=True|MinVolume=1000|DMA200Horizon=15|MinDaysBetweenStopAdjustments=30|MinDaysBetweenInitialStopAdjustment=5|MaxPricingExceptions=3|MACDSetup=(12,26,9)|MACDSignalDays=5|MACDRejectStrongSells=True|MACDRejectWeakSells=True|UseMarketIndicator=True|Benchmark=SPY|BenchmarkMovingAverageDays=200|BenchmarkMovingAverageHorizon=5|UseMarketIndicatorVolatility=True|UseMarketIndicatorVolatilityHorizon=60|UseMarketIndicatorVolatilityBenchmark=^VIX|UseStopLimitScaling=True|StopLimitScalingType=AverageTrueRange|StopLimitScalingVolatilityDays=30|SellOnDMABreak=True|DMABreakValues=200|DMABreakForceBreak=False|EntryType=OverExtended,MVP,PriceTrend,VolumeTrend|EntryHorizon=30|CandidateExpiryDays=180|VolumeTrendDays=10|ChannelBreakoutHorizon=40|UseOverExtendedIndicatorDays=45|UseOverExtendedIndicatorViolationThreshhold=1|UseOverExtendedIndicatorMarginPercent=1|MaxBeta=10|UseMaxBeta=False|UseProfitMaximization=True|UseProfitMaximizationExpression=R_THRESSHOLD=4;MAX_ATR=3;MULTIPLIER=MAX_ATR;IF(RMultiple>=R_THRESSHOLD){MULTIPLIER=1.2;}|UseTradeOnlySectors=False|UseTradeOnlySectorsSectors=Healthcare,Technology,Basic Materials,Consumer Defensive,Industrials|EvaluateStopOnUpTrend=False
|
||||
PricingExceptions=0
|
||||
TotalActivePositions=2
|
||||
Symbol=HWM|PurchaseDate=11/17/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=11|PurchasePrice=200.81|CurrentPrice=257.04|Exposure=2208.91|MarketValue=2827.44|GainLoss=618.53|GainLossPcnt=0.280015935461382|PositionRiskDecimal=0.12|R=24.0336|C=276.15|P=11.490163770721|InitialStopLimit=176.71|TrailingStopLimit=197.56385799408|TotalRiskExposure=264.3696|RMultiple=2.34R|Volatility=3.68232583999634|Volume=0|LastStopAdjustment=1/26/2026 12:00:00 AM|Comment=Price changed on 11/18/2025 from $200.28 to $200.81
|
||||
Symbol=FTI|PurchaseDate=11/20/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=41|PurchasePrice=43.23|CurrentPrice=64.25|Exposure=1772.43|MarketValue=2634.25|GainLoss=861.82|GainLossPcnt=0.486236409900532|PositionRiskDecimal=0.12|R=5.2188|C=218.4595|P=41.8601019391431|InitialStopLimit=38.04|TrailingStopLimit=50.7683570289612|TotalRiskExposure=213.9708|RMultiple=4.03R|Volatility=0.825829267501831|Volume=0|LastStopAdjustment=1/26/2026 12:00:00 AM|Comment=Price changed on 11/21/2025 from $43.49 to $43.23
|
||||
Symbol=HWM|PurchaseDate=11/17/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=11|PurchasePrice=200.81|CurrentPrice=259.64|Exposure=2208.91|MarketValue=2856.04|GainLoss=647.13|GainLossPcnt=0.292963497833773|PositionRiskDecimal=0.12|R=24.0336|C=276.15|P=11.490163770721|InitialStopLimit=176.71|TrailingStopLimit=235.233001537323|TotalRiskExposure=264.3696|RMultiple=2.45R|Volatility=3.68232583999634|Volume=0|LastStopAdjustment=2/25/2026 12:00:00 AM|Comment=Price changed on 11/18/2025 from $200.28 to $200.81
|
||||
Symbol=FTI|PurchaseDate=11/20/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=41|PurchasePrice=43.23|CurrentPrice=66.13|Exposure=1772.43|MarketValue=2711.33|GainLoss=938.9|GainLossPcnt=0.529724728198011|PositionRiskDecimal=0.12|R=5.2188|C=218.4595|P=41.8601019391431|InitialStopLimit=38.04|TrailingStopLimit=62.8357140827179|TotalRiskExposure=213.9708|RMultiple=4.39R|Volatility=0.825829267501831|Volume=0|LastStopAdjustment=2/25/2026 12:00:00 AM|Comment=Price changed on 11/21/2025 from $43.49 to $43.23
|
||||
TotalPositions=133
|
||||
Symbol=CDNS|PurchaseDate=8/25/2020 12:00:00 AM|SellDate=9/3/2020 12:00:00 AM|Shares=16|PurchasePrice=111.82|CurrentPrice=109.57|Exposure=1789.12|MarketValue=1753.12|GainLoss=-36|GainLossPcnt=-0.0201216240386335|PositionRiskDecimal=0.12|R=13.3512|C=225.6365|P=16.9000913775541|InitialStopLimit=97.9088|TrailingStopLimit=109.599856939316|TotalRiskExposure=213.6192|RMultiple=-0.17R|Volatility=2.3209912776947|Volume=1767980|LastStopAdjustment=9/2/2020 12:00:00 AM|Comment=Manual close.
|
||||
Symbol=LULU|PurchaseDate=8/28/2020 12:00:00 AM|SellDate=9/4/2020 12:00:00 AM|Shares=3|PurchasePrice=377.5|CurrentPrice=370.23|Exposure=1132.5|MarketValue=1110.69|GainLoss=-21.8099999999999|GainLossPcnt=-0.0192582781456953|PositionRiskDecimal=0.12|R=45.2976|C=136.6285|P=3.01624147857723|InitialStopLimit=332.1824|TrailingStopLimit=372.562428512573|TotalRiskExposure=135.8928|RMultiple=-0.16R|Volatility=25.858959197998|Volume=2871665|LastStopAdjustment=9/2/2020 12:00:00 AM|Comment=Manual close.
|
||||
@@ -144,7 +144,7 @@ Symbol=DASH|PurchaseDate=10/20/2025 12:00:00 AM|SellDate=11/6/2025 12:00:00 AM|S
|
||||
Symbol=HCI|PurchaseDate=11/6/2025 12:00:00 AM|SellDate=11/17/2025 12:00:00 AM|Shares=12|PurchasePrice=199.2|CurrentPrice=175.62|Exposure=2390.4|MarketValue=2107.44|GainLoss=-282.96|GainLossPcnt=-0.118373493975903|PositionRiskDecimal=0.12|R=23.4012|C=290.298|P=12.4052612686529|InitialStopLimit=175.3|TrailingStopLimit=175.3|TotalRiskExposure=280.8144|RMultiple=-1.01R|Volatility=8.70686626434326|Volume=0|LastStopAdjustment=1/1/0001 12:00:00 AM|Comment=Manual close.
|
||||
Symbol=NFLX|PurchaseDate=10/21/2025 12:00:00 AM|SellDate=11/20/2025 12:00:00 AM|Shares=10|PurchasePrice=114.35|CurrentPrice=105.51|Exposure=1143.5|MarketValue=1055.1|GainLoss=-88.3999999999999|GainLossPcnt=-0.0773065150852644|PositionRiskDecimal=0.12|R=148.962|C=201.1665|P=1.35045514963548|InitialStopLimit=100.62|TrailingStopLimit=105.51|TotalRiskExposure=1489.62|RMultiple=-0.06R|Volatility=19.877721786499|Volume=0|LastStopAdjustment=11/13/2025 12:00:00 AM|Comment=Manual close.
|
||||
Symbol=CX|PurchaseDate=10/20/2025 12:00:00 AM|SellDate=2/5/2026 12:00:00 AM|Shares=144|PurchasePrice=9.54|CurrentPrice=11.74|Exposure=1373.76|MarketValue=1690.56|GainLoss=316.8|GainLossPcnt=0.230607966457023|PositionRiskDecimal=0.12|R=1.1472|C=166.144|P=144.825662482566|InitialStopLimit=8.4|TrailingStopLimit=12.0339285671711|TotalRiskExposure=165.1968|RMultiple=1.92R|Volatility=0.164089068770409|Volume=0|LastStopAdjustment=1/28/2026 12:00:00 AM|Comment=Manual close.
|
||||
TotalCandidates=128
|
||||
TotalCandidates=131
|
||||
Symbol=FINMY|AnalysisDate=10/2/2025 12:00:00 AM|EPSSlope=0.0349999964237213|ProfitMarginSlope=2.26086616516113|PriceSlope=0.00448679936177329|Volatility=1.03268754482269|Volume=0|Violation=False|Slope=0.00448679936177329|Score=2.66255318366838|AnnualizedReturn=3.09774193858814|SharpeRatio=0.335718769738377|RSquared=0.859514199843871|BetaMonths=6|Beta=0.824935419784389
|
||||
Symbol=RYCEY|AnalysisDate=10/1/2025 12:00:00 AM|EPSSlope=0.0249999910593033|ProfitMarginSlope=2.86403560638428|PriceSlope=0.00377742185948191|Volatility=0.317665636539459|Volume=0|Violation=False|Slope=0.00377742185948191|Score=2.3960549250413|AnnualizedReturn=2.59065388368147|SharpeRatio=0.325355777751677|RSquared=0.924884231017524|BetaMonths=6|Beta=1.39611806037306
|
||||
Symbol=TTMI|AnalysisDate=9/23/2025 12:00:00 AM|EPSSlope=0.0699999928474426|ProfitMarginSlope=0.416337013244629|PriceSlope=0.00365635399457596|Volatility=3.10769009590149|Volume=0|Violation=False|Slope=0.00365635399457596|Score=1.73840687121807|AnnualizedReturn=2.5128088887714|SharpeRatio=0.319624993971795|RSquared=0.691818179642002|BetaMonths=6|Beta=4.40428456309766
|
||||
@@ -156,7 +156,6 @@ Symbol=BVN|AnalysisDate=9/22/2025 12:00:00 AM|EPSSlope=0.0400000214576721|Profit
|
||||
Symbol=FN|AnalysisDate=11/4/2025 12:00:00 AM|EPSSlope=0.0999999046325684|ProfitMarginSlope=0.0680141448974609|PriceSlope=0.00254868146461796|Volatility=18.239673614502|Volume=0|Violation=False|Slope=0.00254868146461796|Score=1.12395010145193|AnnualizedReturn=1.90078646425663|SharpeRatio=0.156706951792266|RSquared=0.591307925738777|BetaMonths=6|Beta=1.07114601199124
|
||||
Symbol=BBW|AnalysisDate=9/15/2025 12:00:00 AM|EPSSlope=0.150000095367432|ProfitMarginSlope=0.480964660644531|PriceSlope=0.00200401683880659|Volatility=7.05817651748657|Volume=0|Violation=False|Slope=0.00200401683880659|Score=1.00361947154793|AnnualizedReturn=1.65700580768719|SharpeRatio=0.109950367761114|RSquared=0.605682531039984|BetaMonths=6|Beta=3.21592857727695
|
||||
Symbol=CMCL|AnalysisDate=9/15/2025 12:00:00 AM|EPSSlope=0.310000002384186|ProfitMarginSlope=4.14008331298828|PriceSlope=0.00302127627627187|Volatility=3.12982964515686|Volume=0|Violation=False|Slope=0.00302127627627187|Score=0.951025793020903|AnnualizedReturn=2.14118972672561|SharpeRatio=0.202695139427034|RSquared=0.444157648035817|BetaMonths=6|Beta=2.17029751135873
|
||||
Symbol=CALX|AnalysisDate=8/28/2025 12:00:00 AM|EPSSlope=0.0549999922513962|ProfitMarginSlope=0.674154281616211|PriceSlope=0.00191887831304489|Volatility=1.38379228115082|Volume=0|Violation=False|Slope=0.00191887831304489|Score=0.942723347895125|AnnualizedReturn=1.62183355759321|SharpeRatio=-0.0880265822610902|RSquared=0.581270096109071|BetaMonths=6|Beta=1.94156325892171
|
||||
Symbol=LRCX|AnalysisDate=10/6/2025 12:00:00 AM|EPSSlope=0.284999966621399|ProfitMarginSlope=1.35325241088867|PriceSlope=0.00181712466160022|Volatility=9.07101631164551|Volume=0|Violation=False|Slope=0.00181712466160022|Score=0.897874285800376|AnnualizedReturn=1.58077528709117|SharpeRatio=-0.161869015355145|RSquared=0.567996155514667|BetaMonths=6|Beta=2.15038218212012
|
||||
Symbol=FLGT|AnalysisDate=11/17/2025 12:00:00 AM|EPSSlope=0.134999990463257|ProfitMarginSlope=1.82804107666016|PriceSlope=0.00133612085034311|Volatility=2.98788475990295|Volume=0|Violation=False|Slope=0.00133612085034311|Score=0.823223622834574|AnnualizedReturn=1.40032234183431|SharpeRatio=-0.25967434073425|RSquared=0.58788151716284|BetaMonths=6|Beta=2.6999455316195
|
||||
Symbol=NUS|AnalysisDate=9/18/2025 12:00:00 AM|EPSSlope=1.40000009536743|ProfitMarginSlope=3.08309936523438|PriceSlope=0.00180318893081017|Volatility=0.283407717943192|Volume=0|Violation=False|Slope=0.00180318893081017|Score=0.733289064603337|AnnualizedReturn=1.57523365013054|SharpeRatio=0.0233491292890774|RSquared=0.465511300207795|BetaMonths=6|Beta=2.68947703492498
|
||||
@@ -273,7 +272,11 @@ Symbol=MSGE|AnalysisDate=2/18/2026 12:00:00 AM|EPSSlope=0.190000027418137|Profit
|
||||
Symbol=CRUS|AnalysisDate=2/18/2026 12:00:00 AM|EPSSlope=0.275000095367432|ProfitMarginSlope=0.257987976074219|PriceSlope=0.00138302434593785|Volatility=6.7416615486145|Volume=0|Violation=False|Slope=0.00138302434593785|Score=0.99276144864185|AnnualizedReturn=1.41697190749937|SharpeRatio=-0.160325230400514|RSquared=0.700621828412847|BetaMonths=6|Beta=4.37158193573655
|
||||
Symbol=KN|AnalysisDate=2/20/2026 12:00:00 AM|EPSSlope=0.0450000166893005|ProfitMarginSlope=1.58130264282227|PriceSlope=0.0022407357950537|Volatility=1.25567162036896|Volume=0|Violation=False|Slope=0.0022407357950537|Score=1.48837295417815|AnnualizedReturn=1.75885920577315|SharpeRatio=-0.0777997706291159|RSquared=0.846214949606441|BetaMonths=6|Beta=2.73446817127298
|
||||
Symbol=DHT|AnalysisDate=2/20/2026 12:00:00 AM|EPSSlope=0.0349999666213989|ProfitMarginSlope=7.21533393859863|PriceSlope=0.00126847140872784|Volatility=1.08960521221161|Volume=0|Violation=False|Slope=0.00126847140872784|Score=1.02759147711618|AnnualizedReturn=1.37665245498958|SharpeRatio=-0.164685534611809|RSquared=0.746442192720277|BetaMonths=6|Beta=1.44097492371681
|
||||
TotalStopLimits=211
|
||||
Symbol=ASC|AnalysisDate=2/24/2026 12:00:00 AM|EPSSlope=0.0550000071525574|ProfitMarginSlope=1.1240701675415|PriceSlope=0.00137848718481532|Volatility=0.965735495090485|Volume=0|Violation=False|Slope=0.00137848718481532|Score=0.968432150672692|AnnualizedReturn=1.41535271781552|SharpeRatio=-0.206031334043251|RSquared=0.684233787438784|BetaMonths=6|Beta=2.18573697301378
|
||||
Symbol=AXTI|AnalysisDate=2/25/2026 12:00:00 AM|EPSSlope=0.0100000202655792|ProfitMarginSlope=14.3379011154175|PriceSlope=0.0132443494419251|Volatility=5.61062288284302|Volume=0|Violation=False|Slope=0.0132443494419251|Score=25.5950000737303|AnnualizedReturn=28.150808051139|SharpeRatio=0.668193540464948|RSquared=0.909210138026384|BetaMonths=6|Beta=8.22958399333266
|
||||
Symbol=NSSC|AnalysisDate=2/25/2026 12:00:00 AM|EPSSlope=0.0450000166893005|ProfitMarginSlope=2.89405250549316|PriceSlope=0.00303225543568594|Volatility=1.4112114906311|Volume=0|Violation=False|Slope=0.00303225543568594|Score=1.7760120012008|AnnualizedReturn=2.14712206234386|SharpeRatio=0.039693929353024|RSquared=0.82715930889465|BetaMonths=6|Beta=3.90586565038708
|
||||
Symbol=LPG|AnalysisDate=2/25/2026 12:00:00 AM|EPSSlope=0.299999952316284|ProfitMarginSlope=3.59709167480469|PriceSlope=0.0014348486652486|Volatility=2.03607726097107|Volume=0|Violation=False|Slope=0.0014348486652486|Score=0.646725439489726|AnnualizedReturn=1.43559854049989|SharpeRatio=-0.0995953229975915|RSquared=0.450491847995702|BetaMonths=6|Beta=1.7235174113368
|
||||
TotalStopLimits=213
|
||||
Symbol=CDNS|AnalysisDate=9/2/2020 12:00:00 AM|PreviousStop=97.9088|NewStop=109.599856939316|CurrentPriceLow=113.59|CurrentPriceClose=117.09|PriceTrendIndicatorSlope=0.310654103755951|StopLimitId=
|
||||
Symbol=LULU|AnalysisDate=9/2/2020 12:00:00 AM|PreviousStop=332.1824|NewStop=372.562428512573|CurrentPriceLow=387.08|CurrentPriceClose=398.29|PriceTrendIndicatorSlope=2.77707505226135|StopLimitId=
|
||||
Symbol=MASI|AnalysisDate=10/23/2020 12:00:00 AM|PreviousStop=213.34|NewStop=223.030285377502|CurrentPriceLow=240.68|CurrentPriceClose=244.77|PriceTrendIndicatorSlope=0.191601455211639|StopLimitId=
|
||||
@@ -485,3 +488,5 @@ Symbol=CX|AnalysisDate=12/29/2025 12:00:00 AM|PreviousStop=9.60214291214943|NewS
|
||||
Symbol=HWM|AnalysisDate=1/26/2026 12:00:00 AM|PreviousStop=193.123001594543|NewStop=197.56385799408|CurrentPriceLow=213.96|CurrentPriceClose=215.39|PriceTrendIndicatorSlope=0.691737055778503|StopLimitId=
|
||||
Symbol=FTI|AnalysisDate=1/26/2026 12:00:00 AM|PreviousStop=40.9017861604691|NewStop=50.7683570289612|CurrentPriceLow=54.49|CurrentPriceClose=54.63|PriceTrendIndicatorSlope=0.566473782062531|StopLimitId=
|
||||
Symbol=CX|AnalysisDate=1/28/2026 12:00:00 AM|PreviousStop=10.9233570492268|NewStop=12.0339285671711|CurrentPriceLow=12.9|CurrentPriceClose=13.17|PriceTrendIndicatorSlope=0.0806165412068367|StopLimitId=
|
||||
Symbol=HWM|AnalysisDate=2/25/2026 12:00:00 AM|PreviousStop=197.56385799408|NewStop=235.233001537323|CurrentPriceLow=255.82|CurrentPriceClose=259.64|PriceTrendIndicatorSlope=3.19690990447998|StopLimitId=
|
||||
Symbol=FTI|AnalysisDate=2/25/2026 12:00:00 AM|PreviousStop=50.7683570289612|NewStop=62.8357140827179|CurrentPriceLow=64.83|CurrentPriceClose=66.13|PriceTrendIndicatorSlope=0.521954834461212|StopLimitId=
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
MGSHSESSIONv2.00
|
||||
LastUpdated=2/23/2026 9:37:05 PM
|
||||
TradeDate=2/24/2026
|
||||
LastUpdated=2/25/2026 9:10:49 PM
|
||||
TradeDate=2/26/2026
|
||||
StartDate=3/31/2025
|
||||
AnalysisDate=2/23/2026
|
||||
AnalysisDate=2/25/2026
|
||||
Cycle=11
|
||||
CashBalance=2932.39
|
||||
NonTradeableCash=0
|
||||
HedgeCashBalance=3000
|
||||
Verbose=True|KeepSlotPositions=True|BenchmarkMode=False|BenchmarkModeSymbol=SPY|HoldingPeriod=3|MaxPositions=3|NoTradeSymbols=OSB,IBDRY,GBTC,YOKU,PNY,RFMD,ASAZY|NoTradeFinancialSymbols=U.S. Private Equity,U.S. Financials,U.S. Financial Services,U.S. Banking and Investment Services,Trading-Miscellaneous,Trading--Miscellaneous,Trading--Leveraged Equity,Trading--Leveraged Debt,Trading--Leveraged Commodities,Trading--Inverse Equity,Trading--Inverse Commodities,Tactical Allocation,Specialty Finance,Japan Financials,Savings & Cooperative Banks,Option Writing,Insurance Brokers,Insurance - Specialty,Insurance - Reinsurance,Insurance - Property & Casualty,Insurance - Life,Insurance - Diversified,Global Private Equity,Global Financials,Financial Services,Financial Exchanges,Financial,China Financials,Banks - Regional - US,Banks - Regional - Latin America,Banks - Global,Asset Management,Credit Services|Benchmark=SPY|MarketCapLowerLimit=1000000000|UsePEScreen=False|UseEBITDAScreen=True|UseRevenuePerShareScreen=True|UseLowSlopeBetaCheck=True|LowSlopeBetaDays=15|LowSlopeBetaThreshhold=1|UseMACD=True|MACDSetup=(12,26,9)|MACDSignalDays=12|MACDRejectStrongSellSignals=False|MACDRejectWeakSellSignals=True|UseStochastics=True|StochasticsSignalDays=3|StochasticsRejectStrongSells=True|StochasticsRejectWeakSells=True|UseFallbackCandidate=True|FallbackCandidate=SHV|FallbackCandidateBestOf=SHV,NEAR,BIL,GSY,AGG,ACWX,GSY,SCHF,IXUS,DBEF,IEFA,TLT|UseMaxPEScreen=True|MaxPE=40|StrictMaxPE=False|QualityIndicatorType=IDINDICATOR|IncludeTradeMasterForSymbolsHeld=True|UseStopLimits=True|StopLimitRiskPercentDecimal=0.2|StopLimitScalingVolatilityDays=30|MinDaysBetweenInitialStopAdjustment=30|MinDaysBetweenStopAdjustments=30|StopLimitPriceTrendDays=20|StopLimitATRMultiplier=3|UseHedging=True|HedgeBenchmarkSymbol=SPY|HedgeShortSymbol=SH|HedgeRiskPercentDecimal=0.12|HedgeMinDaysBetweenStopAdjustments=1|HedgeInitialCash=3000|HedgeCloseAboveSMANDays=10|HedgeBandBreakCheckDays=3|HedgeATRMultiplier=1|MaxPricingExceptions=3|UseBetaGenerator=True|UseBetaGeneratorMonths=24
|
||||
TotalActivePositions=7
|
||||
Slot=0|Symbol=USFD|PurchaseDate=9/30/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=12|PurchasePrice=76.21|CurrentPrice=96.13|Volume=2447913|Return1D=0|CumReturn252=0|IDIndicator=-18.7250996015936|Score=0.970794577873616|Velocity=0.708823529411765|PE=33.97|Beta=1.35666771887916|InitialStopLimit=60.97|TrailingStopLimit=90.4222861194611|LastStopAdjustment=2/17/2026 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=15.324|Comment=Price changed on 10/1/2025 from $76.62 to $76.21
|
||||
Slot=0|Symbol=CAH|PurchaseDate=9/30/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=5|PurchasePrice=155.81|CurrentPrice=224.82|Volume=2414935|Return1D=0|CumReturn252=0|IDIndicator=-17.9282868525896|Score=1.45705377610136|Velocity=0.779428953080648|PE=23.64|Beta=0.628226122785001|InitialStopLimit=124.65|TrailingStopLimit=198.937214622498|LastStopAdjustment=1/30/2026 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=31.392|Comment=Price changed on 10/1/2025 from $156.96 to $155.81
|
||||
Slot=0|Symbol=TPB|PurchaseDate=12/31/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=6|PurchasePrice=109|CurrentPrice=135.73|Volume=132784|Return1D=0|CumReturn252=0|IDIndicator=-15.9362549800797|Score=1.44361960940429|Velocity=0.947395388556789|PE=37.1|Beta=1.25054775125095|InitialStopLimit=87.2|TrailingStopLimit=106.557785625458|LastStopAdjustment=1/30/2026 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=21.68|Comment=Price changed on 1/2/2026 from $108.40 to $109.00
|
||||
Slot=1|Symbol=XEL|PurchaseDate=10/31/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=5|PurchasePrice=81.17|CurrentPrice=83.35|Volume=6202750|Return1D=0|CumReturn252=0|IDIndicator=-11.5537848605578|Score=0.602697377681549|Velocity=0.944803580308304|PE=22.43|Beta=-0.192317961128493|InitialStopLimit=64.936|TrailingStopLimit=77.5441425132751|LastStopAdjustment=2/23/2026 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=16.234|Comment=
|
||||
Slot=1|Symbol=MDT|PurchaseDate=1/30/2026 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=14|PurchasePrice=102.85|CurrentPrice=98.61|Volume=6046285|Return1D=0|CumReturn252=0|IDIndicator=-10.3585657370518|Score=0.518428403011861|Velocity=0.865293185419968|PE=26.67|Beta=0.37428296833055|InitialStopLimit=82.28|TrailingStopLimit=82.28|LastStopAdjustment=1/1/0001 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=20.592|Comment=Price changed on 2/2/2026 from $102.96 to $102.85
|
||||
Slot=1|Symbol=ALHC|PurchaseDate=1/30/2026 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=64|PurchasePrice=22.45|CurrentPrice=19.9|Volume=2524769|Return1D=0|CumReturn252=0|IDIndicator=-8.36653386454184|Score=0.341327585226881|Velocity=0.908249807247494|PE=0|Beta=-0.0198043736453601|InitialStopLimit=17.96|TrailingStopLimit=17.96|LastStopAdjustment=1/1/0001 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=4.506|Comment=Price changed on 2/2/2026 from $22.53 to $22.45
|
||||
Slot=2|Symbol=NFG|PurchaseDate=11/28/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=12|PurchasePrice=82.45|CurrentPrice=87.59|Volume=236813|Return1D=0|CumReturn252=0|IDIndicator=-9.56175298804781|Score=1.47874375251707|Velocity=0.658599827139153|PE=31.63|Beta=0.0443025382529758|InitialStopLimit=65.96|TrailingStopLimit=77.9947861814499|LastStopAdjustment=1/30/2026 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=16.49|Comment=
|
||||
Slot=0|Symbol=USFD|PurchaseDate=9/30/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=12|PurchasePrice=76.21|CurrentPrice=95.88|Volume=2447913|Return1D=0|CumReturn252=0|IDIndicator=-18.7250996015936|Score=0.970794577873616|Velocity=0.708823529411765|PE=33.97|Beta=1.35666771887916|InitialStopLimit=60.97|TrailingStopLimit=90.4222861194611|LastStopAdjustment=2/17/2026 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=15.324|Comment=Price changed on 10/1/2025 from $76.62 to $76.21
|
||||
Slot=0|Symbol=CAH|PurchaseDate=9/30/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=5|PurchasePrice=155.81|CurrentPrice=226.18|Volume=2414935|Return1D=0|CumReturn252=0|IDIndicator=-17.9282868525896|Score=1.45705377610136|Velocity=0.779428953080648|PE=23.64|Beta=0.628226122785001|InitialStopLimit=124.65|TrailingStopLimit=198.937214622498|LastStopAdjustment=1/30/2026 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=31.392|Comment=Price changed on 10/1/2025 from $156.96 to $155.81
|
||||
Slot=0|Symbol=TPB|PurchaseDate=12/31/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=6|PurchasePrice=109|CurrentPrice=142.98|Volume=132784|Return1D=0|CumReturn252=0|IDIndicator=-15.9362549800797|Score=1.44361960940429|Velocity=0.947395388556789|PE=37.1|Beta=1.25054775125095|InitialStopLimit=87.2|TrailingStopLimit=106.557785625458|LastStopAdjustment=1/30/2026 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=21.68|Comment=Price changed on 1/2/2026 from $108.40 to $109.00
|
||||
Slot=1|Symbol=XEL|PurchaseDate=10/31/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=5|PurchasePrice=81.17|CurrentPrice=83.55|Volume=6202750|Return1D=0|CumReturn252=0|IDIndicator=-11.5537848605578|Score=0.602697377681549|Velocity=0.944803580308304|PE=22.43|Beta=-0.192317961128493|InitialStopLimit=64.936|TrailingStopLimit=77.5441425132751|LastStopAdjustment=2/23/2026 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=16.234|Comment=
|
||||
Slot=1|Symbol=MDT|PurchaseDate=1/30/2026 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=14|PurchasePrice=102.85|CurrentPrice=96.65|Volume=6046285|Return1D=0|CumReturn252=0|IDIndicator=-10.3585657370518|Score=0.518428403011861|Velocity=0.865293185419968|PE=26.67|Beta=0.37428296833055|InitialStopLimit=82.28|TrailingStopLimit=82.28|LastStopAdjustment=1/1/0001 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=20.592|Comment=Price changed on 2/2/2026 from $102.96 to $102.85
|
||||
Slot=1|Symbol=ALHC|PurchaseDate=1/30/2026 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=64|PurchasePrice=22.45|CurrentPrice=20.13|Volume=2524769|Return1D=0|CumReturn252=0|IDIndicator=-8.36653386454184|Score=0.341327585226881|Velocity=0.908249807247494|PE=0|Beta=-0.0198043736453601|InitialStopLimit=17.96|TrailingStopLimit=17.96|LastStopAdjustment=1/1/0001 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=4.506|Comment=Price changed on 2/2/2026 from $22.53 to $22.45
|
||||
Slot=2|Symbol=NFG|PurchaseDate=11/28/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=12|PurchasePrice=82.45|CurrentPrice=88.48|Volume=236813|Return1D=0|CumReturn252=0|IDIndicator=-9.56175298804781|Score=1.47874375251707|Velocity=0.658599827139153|PE=31.63|Beta=0.0443025382529758|InitialStopLimit=65.96|TrailingStopLimit=77.9947861814499|LastStopAdjustment=1/30/2026 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=16.49|Comment=
|
||||
TotalPositions=17
|
||||
Symbol=MO|PurchaseDate=3/31/2025 12:00:00 AM|SellDate=5/14/2025 12:00:00 AM|Shares=18|PurchasePrice=59.91|CurrentPrice=56.15|Volume=17335180|Return1D=0|CumReturn252=0|IDIndicator=-15.9362549800797|Score=1.14749269300042|Velocity=0.967136150234742|PE=9|Beta=0.572465642401382|InitialStopLimit=47.93|TrailingStopLimit=56.1565003347397|LastStopAdjustment=5/7/2025 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=12.004|Comment=Closed due to trailing stop.
|
||||
Symbol=EXC|PurchaseDate=3/31/2025 12:00:00 AM|SellDate=5/14/2025 12:00:00 AM|Shares=24|PurchasePrice=45.76|CurrentPrice=42.6|Volume=14993121|Return1D=0|CumReturn252=0|IDIndicator=-8.76494023904382|Score=0.405636492837393|Velocity=1|PE=18.02|Beta=0.248374476251328|InitialStopLimit=36.61|TrailingStopLimit=42.7107857322693|LastStopAdjustment=4/30/2025 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=9.216|Comment=Closed due to trailing stop.
|
||||
|
||||
Reference in New Issue
Block a user