24 Commits

Author SHA1 Message Date
d4c97aaf0a Fix Point Mapping. 2026-03-10 23:42:21 -04:00
05b9ac3897 Merge branch 'MKDT_CNN01' 2026-03-10 21:45:50 -04:00
922f99f061 Daily 2026-03-10 21:31:04 -04:00
5c1dd6b2c5 Ensure GetModelPerformance does not short return when a price is missing. 2026-03-10 12:47:14 -04:00
9062965aa0 Daily 2026-03-09 20:39:47 -04:00
8a8a02e928 Daily 2026-03-06 21:30:07 -05:00
21252bf3b3 Daily 2026-03-05 20:12:24 -05:00
da2d87ee7f Daily 2026-03-04 20:48:17 -05:00
804504da84 Daily 2026-03-03 20:15:15 -05:00
2d4dcd5855 Daily 2026-03-02 21:51:37 -05:00
9bfedcf215 Monthly 2026-03-02 18:24:19 -05:00
4783cd3cd1 Monthly 2026-03-02 18:24:03 -05:00
4396a46c9f Initial Commit 2026-02-27 22:28:44 -05:00
9db057e873 Sell RAPT 2026-02-27 20:42:45 -05:00
5c2d512280 Daily 2026-02-27 20:41:19 -05:00
596b183d7e GLPriceCache Dispose shoud exit quickly 2026-02-27 20:33:30 -05:00
0b2350812c Enahnce GLPirceCache. Eviction logic and better synchronizartion. 2026-02-26 22:25:47 -05:00
67aa1bd0cf Merge MKDT_0003 2026-02-26 16:51:29 -05:00
2f9c01e5a0 Fix GLPriceCache 2026-02-26 16:24:35 -05:00
d0eb2f38f6 Daily 2026-02-25 21:14:17 -05:00
860167b3d3 Daily 2026-02-24 21:50:48 -05:00
bffff6c296 Refactor GlobalPriceCache and LocalPriceCache to pattern snapshot, fetch, update. Parellelize. 2026-02-24 12:23:38 -05:00
faded8cd55 Daily 2026-02-23 21:39:04 -05:00
09525546bf Daily 2026-02-20 21:51:43 -05:00
19 changed files with 1340 additions and 428 deletions

View File

@@ -25,11 +25,17 @@ namespace MarketData.CNNProcessing
// MapPoint will both scale the given point and translate the given point from an upper left origin system to a bottom left origin system
public Point MapPoint(Point sourcePoint)
{
Point mappedPoint=new Point((int)((sourcePoint.X-XDataExtentMin)*XScalingFactor),(int)(Height-((sourcePoint.Y-YDataExtentMin)*YScalingFactor)));
mappedPoint.X+=(int)XMargin; // offset by the xMargin
mappedPoint.Y-=(int)YMargin; // offset by the yMargin
return mappedPoint;
int x = (int)((sourcePoint.X - XDataExtentMin) * XScalingFactor + XMargin);
int y = (int)(Height - 1 - ((sourcePoint.Y - YDataExtentMin) * YScalingFactor) - YMargin);
// clamp to bounds
x = Math.Max(0, Math.Min(x, (int)Width - 1));
y = Math.Max(0, Math.Min(y, (int)Height - 1));
return new Point(x, y);
}
// TranslatePoint will only translate the given point from an upper left origin system to a bottom left origin system
public Point TranslatePoint(Point sourcePoint)
{

View File

@@ -1,260 +1,323 @@
using System;
using System.Collections.Generic;
using System.Text;
using MarketData.MarketDataModel;
using MarketData.DataAccess;
using MarketData.Utils;
using System.Linq;
using System.Threading;
using MarketData.MarketDataModel;
using MarketData.Utils;
using MarketData.Helper;
using MarketData.Numerical;
using System.Threading;
using MarketData.DataAccess;
// This cache is mainly used by the models. It is a short lived cache that gets cleared out every 2 minutes.
// This cache will attempt to load a price from the database if it is found in the cache.
namespace MarketData.Cache
{
public class GBPriceCache
public interface IPricingDataAccess
{
private Thread cacheMonitorThread=null;
private volatile bool threadRun=true;
private Object thisLock=new Object();
private Dictionary<String,PricesByDate> priceCache=new Dictionary<String,PricesByDate>(); // the main cache
private Dictionary<String,Price> realTimePriceCache=new Dictionary<String,Price>(); // short lived cache of realtime prices gets cleared out every cacheRefreshAfter(ms)
private Dictionary<String,bool> nullCache=new Dictionary<String,bool>();
private DateGenerator dateGenerator=new DateGenerator();
private static GBPriceCache priceCacheInstance=null;
private int cacheRefreshAfter=120000; // the cache will be cleaned up after 2 minutes
Price GetPrice(string symbol, DateTime date);
Prices GetPrices(string symbol, DateTime maxDate, DateTime minDate);
DateTime GetLatestDateOnOrBefore(string symbol, DateTime date);
}
internal class RealPricingDA : IPricingDataAccess
{
public Price GetPrice(string symbol, DateTime date) => PricingDA.GetPrice(symbol, date);
public Prices GetPrices(string symbol, DateTime maxDate, DateTime minDate) => PricingDA.GetPrices(symbol, maxDate, minDate);
public DateTime GetLatestDateOnOrBefore(string symbol, DateTime date) => PricingDA.GetLatestDateOnOrBefore(symbol, date);
}
internal class CacheSnapshot
{
public Dictionary<String, PricesByDate> PriceCache { get; }
public Dictionary<String, Price> RealTimePriceCache { get; }
public Dictionary<String, bool> NullCache { get; }
public CacheSnapshot(
Dictionary<String, PricesByDate> priceCache,
Dictionary<String, Price> realTimePriceCache,
Dictionary<String, bool> nullCache)
{
PriceCache = priceCache;
RealTimePriceCache = realTimePriceCache;
NullCache = nullCache;
}
}
public class GBPriceCache : IDisposable
{
private Thread cacheMonitorThread = null;
private volatile bool threadRun = true;
private Object thisLock = new Object();
private CacheSnapshot snapshot;
private DateGenerator dateGenerator = new DateGenerator();
private static GBPriceCache priceCacheInstance = null;
private int cacheRefreshAfter = 120000; // 2 minutes
private SemaphoreSlim fetchSemaphore = new SemaphoreSlim(8); // max 8 concurrent DB fetches
public IPricingDataAccess PricingDataAccess { get; set; } = new RealPricingDA();
protected GBPriceCache()
{
cacheMonitorThread=new Thread(new ThreadStart(ThreadProc));
snapshot = new CacheSnapshot(new Dictionary<String, PricesByDate>(), new Dictionary<String, Price>(), new Dictionary<String, bool>());
cacheMonitorThread = new Thread(new ThreadStart(ThreadProc));
cacheMonitorThread.Start();
}
public static GBPriceCache GetInstance()
{
lock(typeof(GBPriceCache))
lock (typeof(GBPriceCache))
{
if(null==priceCacheInstance)
if (null == priceCacheInstance)
{
priceCacheInstance=new GBPriceCache();
priceCacheInstance = new GBPriceCache();
}
return priceCacheInstance;
}
}
public void Clear()
{
lock(thisLock)
lock (thisLock)
{
priceCache=new Dictionary<String,PricesByDate>();
realTimePriceCache=new Dictionary<String,Price>();
nullCache=new Dictionary<String,bool>();
}
}
public void Dispose()
{
lock(thisLock)
{
if(null==priceCacheInstance || false==threadRun)return;
threadRun=false;
if(null!=cacheMonitorThread)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[GBPriceCache:Dispose]Thread state is '{0}'. Joining main thread...",Utility.ThreadStateToString(cacheMonitorThread)));
cacheMonitorThread.Join(5000);
this.cacheMonitorThread=null;
}
MDTrace.WriteLine(LogLevel.DEBUG,"[GBPriceCache:Dispose] End.");
priceCacheInstance=null;
}
}
public void ClearCacheOnOrBefore(DateTime onOrBeforeDate,bool collect=false)
{
lock(thisLock)
{
MDTrace.WriteLine(LogLevel.DEBUG,"Clearing GBPriceCache cache.");
List<String> symbols=new List<String>(priceCache.Keys);
foreach(String symbol in symbols)
{
PricesByDate pricesByDate=priceCache[symbol];
List<DateTime> symbolDates=new List<DateTime>(pricesByDate.Keys);
foreach(DateTime symbolDate in symbolDates)
{
if(symbolDate<onOrBeforeDate) pricesByDate.Remove(symbolDate);
}
}
MDTrace.WriteLine(LogLevel.DEBUG,"Calling garbage collector...");
if(collect) GC.Collect();
snapshot = new CacheSnapshot(new Dictionary<String, PricesByDate>(), new Dictionary<String, Price>(), new Dictionary<String, bool>());
}
}
public Price GetPriceOrLatestAvailable(String symbol,DateTime date)
public void Dispose()
{
lock(thisLock)
lock (thisLock)
{
Price price=GetPrice(symbol,date);
if(null!=price) return price;
DateTime latestPricingDate=PricingDA.GetLatestDateOnOrBefore(symbol,date);
price=GetPrice(symbol,latestPricingDate);
if(null!=price) return price;
price=PricingDA.GetPrice(symbol,latestPricingDate);
if(null!=price) AddPrice(price);
return price;
if (null == priceCacheInstance || !threadRun) return;
threadRun = false;
if (null != cacheMonitorThread)
{
MDTrace.WriteLine(LogLevel.DEBUG, "[GBPriceCache:Dispose] Joining monitor thread...");
cacheMonitorThread.Join(5000);
cacheMonitorThread = null;
}
priceCacheInstance = null;
}
}
public void ClearCacheOnOrBefore(DateTime onOrBeforeDate, bool collect = false)
{
lock (thisLock)
{
Dictionary<String, PricesByDate> newPriceCache = new Dictionary<String, PricesByDate>();
foreach (KeyValuePair<String, PricesByDate> entry in snapshot.PriceCache)
{
String symbol = entry.Key;
PricesByDate filteredPrices = new PricesByDate();
PricesByDate existingPrices = entry.Value;
foreach (KeyValuePair<DateTime, Price> kvp in existingPrices)
{
if (kvp.Key >= onOrBeforeDate)
{
filteredPrices.Add(kvp.Key, kvp.Value);
}
}
if (filteredPrices.Count > 0)
{
newPriceCache.Add(symbol, filteredPrices);
}
}
UpdateSnapshot(newPriceCache, snapshot.RealTimePriceCache, snapshot.NullCache);
if (collect) GC.Collect();
}
}
public Price GetPriceOrLatestAvailable(String symbol, DateTime date)
{
Price price = GetPrice(symbol, date);
if (null != price) return price;
DateTime latestPricingDate = PricingDataAccess.GetLatestDateOnOrBefore(symbol, date);
price = GetPrice(symbol, latestPricingDate);
if (null != price) return price;
fetchSemaphore.Wait();
try
{
price = PricingDataAccess.GetPrice(symbol, latestPricingDate);
}
finally
{
fetchSemaphore.Release();
}
if (null !=price) AddPrice(price);
return price;
}
public Price GetRealtimePrice(String symbol)
{
if(realTimePriceCache.ContainsKey(symbol)) return realTimePriceCache[symbol];
Price price=MarketDataHelper.GetLatestPrice(symbol);
if(null!=price)
if (snapshot.RealTimePriceCache.ContainsKey(symbol))
{
realTimePriceCache.Add(symbol,price);
return snapshot.RealTimePriceCache[symbol];
}
Price price = MarketDataHelper.GetLatestPrice(symbol);
if (null != price)
{
Dictionary<String, Price> newRealtime = new Dictionary<String, Price>(snapshot.RealTimePriceCache);
newRealtime.Add(symbol, price);
UpdateSnapshot(snapshot.PriceCache, newRealtime, snapshot.NullCache);
}
return price;
}
public Price GetPrice(String symbol,DateTime date)
public Price GetPrice(String symbol, DateTime date)
{
lock(thisLock)
date = date.Date;
if (!ContainsPrice(symbol, date))
{
date=date.Date;
if(!ContainsPrice(symbol,date))
String key = symbol + Utility.DateTimeToStringMMHDDHYYYY(date);
if (snapshot.NullCache.ContainsKey(key))
{
String key=symbol+Utility.DateTimeToStringMMHDDHYYYY(date);
if(nullCache.ContainsKey(key)) return null;
Price price=PricingDA.GetPrice(symbol,date);
if(null==price)
{
nullCache.Add(key,true);
return price;
}
AddPrice(price);
return null;
}
if(!priceCache.ContainsKey(symbol)) return null;
PricesByDate pricesByDate=priceCache[symbol];
if(!pricesByDate.ContainsKey(date.Date)) return null;
return pricesByDate[date];
fetchSemaphore.Wait();
Price price;
try
{
price = PricingDataAccess.GetPrice(symbol, date);
}
finally
{
fetchSemaphore.Release();
}
if (null ==price)
{
Dictionary<String, bool> newNullCache = new Dictionary<String, bool>(snapshot.NullCache);
newNullCache.Add(key, true);
UpdateSnapshot(snapshot.PriceCache, snapshot.RealTimePriceCache, newNullCache);
return null;
}
AddPrice(price);
}
if (!snapshot.PriceCache.ContainsKey(symbol)) return null;
PricesByDate pricesByDate = snapshot.PriceCache[symbol];
if (!pricesByDate.ContainsKey(date)) return null;
return pricesByDate[date];
}
public Prices GetPrices(String symbol, DateTime earlierDate, DateTime laterDate)
{
DateGenerator dateGenerator = new DateGenerator();
if(laterDate<earlierDate)
DateGenerator localDateGenerator = new DateGenerator();
if (laterDate < earlierDate)
{
DateTime tempDate = earlierDate;
earlierDate = laterDate;
laterDate=tempDate;
laterDate = tempDate;
}
List<DateTime> datesList = dateGenerator.GenerateHistoricalDates(earlierDate, laterDate);
List<DateTime> datesList = localDateGenerator.GenerateHistoricalDates(earlierDate, laterDate);
datesList = datesList.Where(x => x >= earlierDate).ToList();
return GetPrices(symbol, laterDate, datesList.Count);
}
// The most recent price is returned at the lowest index
public Prices GetPrices(String symbol,DateTime startDate,int dayCount)
public Prices GetPrices(String symbol, DateTime startDate, int dayCount)
{
lock(thisLock)
List<DateTime> historicalDates = dateGenerator.GenerateHistoricalDates(startDate, dayCount + 60);
List<DateTime> missingDates = new List<DateTime>();
foreach (DateTime historicalDate in historicalDates)
{
List<DateTime> historicalDates=dateGenerator.GenerateHistoricalDates(startDate,dayCount+60);
Prices prices=null;
List<DateTime> missingDates=null;
foreach(DateTime historicalDate in historicalDates)
if (!ContainsPrice(symbol, historicalDate))
{
if(!ContainsPrice(symbol,historicalDate))
String key = symbol + Utility.DateTimeToStringMMHDDHYYYY(historicalDate);
if (!snapshot.NullCache.ContainsKey(key))
{
String key=symbol+Utility.DateTimeToStringMMHDDHYYYY(historicalDate);
if(nullCache.ContainsKey(key)) continue;
if(null==missingDates)missingDates=new List<DateTime>();
missingDates.Add(historicalDate);
}
}
if(null!=missingDates)
{
DateTime minDate=(from DateTime date in missingDates select date).Min();
DateTime maxDate=(from DateTime date in missingDates select date).Max();
prices=PricingDA.GetPrices(symbol,maxDate,minDate);
foreach(Price price in prices) AddPrice(price);
prices=new Prices();
foreach(DateTime historicalDate in historicalDates)
{
if(!ContainsPrice(symbol,historicalDate))
{
String key=symbol+Utility.DateTimeToStringMMHDDHYYYY(historicalDate);
if(!nullCache.ContainsKey(key)) nullCache.Add(key,true);
}
else
{
if(!priceCache.ContainsKey(symbol)) continue;
PricesByDate pricesByDate=priceCache[symbol];
if(!pricesByDate.ContainsKey(historicalDate.Date)) continue;
prices.Add(pricesByDate[historicalDate]);
}
}
}
else
{
prices=new Prices();
foreach(DateTime historicalDate in historicalDates)
{
if(!priceCache.ContainsKey(symbol)) continue;
if(!priceCache[symbol].ContainsKey(historicalDate.Date))
{
continue;
}
prices.Add((priceCache[symbol])[historicalDate]);
}
}
return new Prices(prices.OrderByDescending(x => x.Date).ToList().Take(dayCount).ToList());
}
if (missingDates.Count > 0)
{
DateTime minDate = missingDates.Min();
DateTime maxDate = missingDates.Max();
fetchSemaphore.Wait();
Prices loadedPrices;
try
{
loadedPrices = PricingDataAccess.GetPrices(symbol, maxDate, minDate);
}
finally
{
fetchSemaphore.Release();
}
foreach (Price price in loadedPrices)
{
AddPrice(price);
}
}
Prices prices = new Prices();
foreach (DateTime historicalDate in historicalDates)
{
if (!snapshot.PriceCache.ContainsKey(symbol)) continue;
PricesByDate pricesByDate = snapshot.PriceCache[symbol];
if (!pricesByDate.ContainsKey(historicalDate)) continue;
prices.Add(pricesByDate[historicalDate]);
}
List<Price> ordered = prices.OrderByDescending(x => x.Date).ToList();
return new Prices(ordered.Take(dayCount).ToList());
}
private void AddPrice(Price price)
{
lock(thisLock)
if (null == price) return;
lock (thisLock)
{
if(null==price) return;
PricesByDate pricesByDate=null;
if(!priceCache.ContainsKey(price.Symbol))
PricesByDate pricesByDate;
if (!snapshot.PriceCache.ContainsKey(price.Symbol))
{
pricesByDate=new PricesByDate();
pricesByDate.Add(price.Date,price);
priceCache.Add(price.Symbol,pricesByDate);
pricesByDate = new PricesByDate();
pricesByDate.Add(price.Date, price);
Dictionary<String, PricesByDate> newCache = new Dictionary<String, PricesByDate>(snapshot.PriceCache);
newCache.Add(price.Symbol, pricesByDate);
UpdateSnapshot(newCache, snapshot.RealTimePriceCache, snapshot.NullCache);
}
else
{
pricesByDate=priceCache[price.Symbol];
if(pricesByDate.ContainsKey(price.Date.Date)) return;
pricesByDate.Add(price.Date.Date,price);
}
}
}
public bool ContainsPrice(String symbol,DateTime date)
{
if(!priceCache.ContainsKey(symbol)) return false;
PricesByDate pricesByDate=priceCache[symbol];
if(!pricesByDate.ContainsKey(date.Date)) return false;
return true;
}
private void ThreadProc()
{
int quantums=0;
int quantumInterval=1000;
while(threadRun)
{
Thread.Sleep(quantumInterval);
if(!threadRun) break;
quantums+=quantumInterval;
if(quantums>cacheRefreshAfter)
{
quantums=0;
lock(thisLock)
pricesByDate = snapshot.PriceCache[price.Symbol];
if (!pricesByDate.ContainsKey(price.Date))
{
realTimePriceCache.Clear();
MDTrace.WriteLine(LogLevel.DEBUG,"Clearing GBPriceCache price cache.");
pricesByDate.Add(price.Date, price);
}
}
}
MDTrace.WriteLine(LogLevel.DEBUG,"[GBPriceCache:ThreadProc]Thread ended.");
}
public bool ContainsPrice(String symbol, DateTime date)
{
if (!snapshot.PriceCache.ContainsKey(symbol)) return false;
PricesByDate pricesByDate = snapshot.PriceCache[symbol];
return pricesByDate.ContainsKey(date);
}
private void ThreadProc()
{
int quantums = 0;
int quantumInterval = 1000;
while (threadRun)
{
Thread.Sleep(quantumInterval);
if(!threadRun)break;
quantums += quantumInterval;
if (quantums > cacheRefreshAfter)
{
quantums = 0;
lock (thisLock)
{
UpdateSnapshot(snapshot.PriceCache, new Dictionary<String, Price>(), snapshot.NullCache);
}
}
}
}
private void UpdateSnapshot(Dictionary<String, PricesByDate> newPriceCache,Dictionary<String, Price> newRealtimePriceCache, Dictionary<String, bool> newNullCache)
{
snapshot = new CacheSnapshot(newPriceCache, newRealtimePriceCache, newNullCache);
}
}
}

View File

@@ -0,0 +1,706 @@
 using MarketData.MarketDataModel;
using MarketData.Utils;
using MarketData.DataAccess;
using System.Collections.Concurrent;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Linq;
using System.Threading.Tasks;
namespace MarketData.Cache
{
/// <summary>
/// GLPriceCache - Used by Gain/Loss Generator which are usedby MarketDataSErver and the User Interface.
/// The entire cache evicts every hour so calling Add(PortfolioTrades portfolioTrades) will force price reload
/// Always ensures that open trade symbols get most recent price from the database.
/// </summary>
public class GLPriceCache : IDisposable
{
// -- Singleton ------------------------------------------------------------
private static readonly object instanceLock = new object();
private static GLPriceCache instance = null;
// -- Disposal -------------------------------------------------------------
private volatile bool disposed = false;
private int refreshInProgress = 0;
/// <summary>
///
/// </summary>
/// <returns></returns>
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()
{
return Math.Min(Math.Max(1, Environment.ProcessorCount) * 3, 32);
// 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;
}
// -- 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 = null;
lock (instanceLock)
{
timerToDispose = refreshTimer;
refreshTimer = null;
instance = null;
}
// Dispose timer immediately; no blocking wait
timerToDispose?.Dispose();
MDTrace.WriteLine(LogLevel.DEBUG, "[GLPriceCache:Dispose] Disposed.");
}
}
/// <summary>
/// Dispoal
/// </summary>
//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 (ManualResetEventSlim 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(PortfolioTrades portfolioTrades)
{
List<string> symbols = portfolioTrades.Symbols;
// Map each symbol to its earliest trade date
Dictionary<string, DateTime> minTradeDates = symbols.ToDictionary(symbol => symbol, symbol => portfolioTrades.GetMinTradeDate(symbol));
// Only open positions need intraday price refresh
HashSet<string> mutableSymbols = new HashSet<string>(symbols.Where(sym => portfolioTrades.HasOpenPositions(sym)));
// Pre-filter symbols to skip fully cached immutable symbols
List<string> symbolsToProcess = symbols
.Where(sym =>
{
DateTime minCacheDate;
lock (cacheLock)
{
minCacheDate = priceCache.ContainsKey(sym) ? priceCache[sym].MinDate : DateTime.MaxValue;
}
DateTime minTradeDate = minTradeDates[sym];
// Skip if symbol is immutable AND cache already covers all trade dates
return mutableSymbols.Contains(sym) || minTradeDate < minCacheDate;
})
.ToList();
if (symbolsToProcess.Count == 0) return; // nothing to do
ConcurrentDictionary<string, Prices> fetchedPrices = new ConcurrentDictionary<string, Prices>();
ConcurrentDictionary<string, Price> latestPrices = new ConcurrentDictionary<string, Price>();
Parallel.ForEach(symbolsToProcess, new ParallelOptions { MaxDegreeOfParallelism = maxParallelDbCalls }, symbol =>
{
if (disposed) return;
DateTime minTradeDate = minTradeDates[symbol];
DateTime minCacheDate;
lock (cacheLock)
{
minCacheDate = priceCache.ContainsKey(symbol) ? priceCache[symbol].MinDate : DateTime.MaxValue;
}
object symbolLock = symbolFetchLocks.GetOrAdd(symbol, _ => new object());
lock (symbolLock)
{
try
{
// Re-check cache after acquiring lock
lock (cacheLock)
{
if (priceCache.ContainsKey(symbol))minCacheDate = priceCache[symbol].MinDate;
}
Prices prices = null;
// Historical fetch only if cache is missing or incomplete
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 for 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}");
}
} // lcok(symLock)
}); // Parallel
lock (cacheLock)
{
// Historical prices — idempotent, do 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);
}
}
// Only log if we actually fetched or updated something
if (fetchedPrices.Count > 0 || latestPrices.Count > 0)
{
MDTrace.WriteLine(LogLevel.DEBUG,$"[GLPriceCache:Add] Symbols processed: {symbolsToProcess.Count}, Mutable: {mutableSymbols.Count}, " +
$"Historical fetches: {fetchedPrices.Count}, Intraday updates: {latestPrices.Count}");
}
}
/// <summary>
/// Add prices
/// </summary>
/// <param name="prices"></param>
public void Add(Prices prices)
{
foreach (Price price in prices)Add(price);
}
/// <summary>
/// Add(List<string> symbols, DateTime pricingDate)
/// </summary>
/// <param name="symbols"></param>
/// <param name="pricingDate"></param>
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);
}
}
}
/// <summary>
/// Add(Price price)
/// </summary>
/// <param name="price"></param>
public void Add(Price price)
{
lock (cacheLock)
{
AddInternal(price);
}
}
/// <summary>
/// Refresh
/// </summary>
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}");
}
}); // Parallel
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);
}
}
} // lock(cacheLock)
MDTrace.WriteLine(LogLevel.DEBUG,$"[GLPriceCache:Refresh] Full reloads: {fullReloads.Count}, Single updates: {singleUpdates.Count}");
}
finally
{
Interlocked.Exchange(ref refreshInProgress, 0);
}
}
/// <summary>
/// GetLatestDate
/// </summary>
/// <returns></returns>
public DateTime GetLatestDate()
{
lock (cacheLock)
{
if (Utility.IsEpoch(latestDate))
{
latestDate = PricingDA.GetLatestDate();
}
return latestDate;
}
}
/// <summary>
/// RefreshLatestDate
/// </summary>
public void RefreshLatestDate()
{
lock (cacheLock)
{
latestDate = PricingDA.GetLatestDate();
}
}
/// <summary>
/// GetMinCacheDate
/// </summary>
/// <param name="symbol"></param>
/// <returns></returns>
public DateTime GetMinCacheDate(string symbol)
{
lock (cacheLock)
{
PricesByDate symbolPrices;
if (!priceCache.TryGetValue(symbol, out symbolPrices) || symbolPrices.Count == 0)
{
return Utility.Epoch;
}
return symbolPrices.MinDate;
}
}
/// <summary>
/// GetPrices
/// </summary>
/// <param name="symbol"></param>
/// <param name="endDate"></param>
/// <param name="dayCount"></param>
/// <returns></returns>
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;
} // lock(cacheLock)
}
/// <summary>
/// GetPrice
/// </summary>
/// <param name="symbol"></param>
/// <param name="date"></param>
/// <returns></returns>
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;
}
}
/// <summary>
/// ContainsPrice
/// </summary>
/// <param name="symbol"></param>
/// <param name="date"></param>
/// <returns></returns>
public bool ContainsPrice(string symbol, DateTime date)
{
lock (cacheLock)
{
return ContainsPriceInternal(symbol, date);
}
}
/// <summary>
/// ContainsPrice
/// </summary>
/// <param name="symbols"></param>
/// <param name="date"></param>
/// <returns></returns>
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;
}
}
/// <summary>
/// ContainsSymbol
/// </summary>
/// <param name="symbol"></param>
/// <returns></returns>
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);
}
}
/// <summary>
/// ContainsPriceInternal - mUST BE CALLED UNDER CACHELOCK
/// </summary>
/// <param name="symbol"></param>
/// <param name="date"></param>
/// <returns></returns>
private bool ContainsPriceInternal(string symbol, DateTime date)
{
PricesByDate pricesByDate;
if (!priceCache.TryGetValue(symbol, out pricesByDate)) return false;
return pricesByDate.ContainsKey(date);
}
/// <summary>
/// CountInternal - MUST BE CALLED UNDER CACHELOCK
/// </summary>
/// <returns></returns>
private long CountInternal()
{
long count = 0;
foreach (PricesByDate pricesByDate in priceCache.Values)
{
count += pricesByDate.Count;
}
return count;
}
}
}

View File

@@ -1,65 +1,70 @@
using System;
using System.Linq;
using System.Collections.Generic;
using MarketData.MarketDataModel;
using MarketData.MarketDataModel;
using MarketData.Utils;
using MarketData.DataAccess;
using System.Collections.Concurrent;
using System.Threading;
using System.Collections.Generic;
using System;
using System.Threading.Tasks;
using System.Linq;
// This cache is mainly used by gainloss generator. This cache is intended to be front loaded and then used.
// This cache will not attempt to load an item that is not found. It does have a Refresh() that will reload only the most recent pricing data from the database in order to
// maintain the most updated pricing.
namespace MarketData.Cache
{
public class LocalPriceCache
{
private Dictionary<String,PricesByDate> priceCache=new Dictionary<String,PricesByDate>();
private static LocalPriceCache instance=null;
public class LocalPriceCache
{
private Dictionary<string, PricesByDate> priceCache = new Dictionary<string, PricesByDate>();
private static LocalPriceCache instance = null;
private DateTime latestDate = Utility.Epoch;
private Thread cacheMonitorThread=null;
private volatile bool threadRun=true;
private int cacheCycle=300000;
private Object thisLock=new Object();
private Thread cacheMonitorThread = null;
private volatile bool threadRun = true;
private int cacheCycle = 300000;
private object thisLock = new object();
private object fetchLock = new object();
private LocalPriceCache()
{
cacheMonitorThread=new Thread(new ThreadStart(ThreadProc));
private LocalPriceCache()
{
cacheMonitorThread = new Thread(new ThreadStart(ThreadProc));
cacheMonitorThread.Start();
}
}
public void Clear()
{
lock(thisLock)
lock (thisLock)
{
priceCache=new Dictionary<String,PricesByDate>();
priceCache = new Dictionary<string, PricesByDate>();
RefreshLatestDate();
}
}
public void Dispose()
{
lock(thisLock)
Thread threadToJoin = null;
lock (thisLock)
{
if(null==instance || false==threadRun)return;
threadRun=false;
if(null!=cacheMonitorThread)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[LocalPriceCache:Dispose]Thread state is '{0}'. Joining main thread...",Utility.ThreadStateToString(cacheMonitorThread)));
cacheMonitorThread.Join(5000);
this.cacheMonitorThread=null;
}
MDTrace.WriteLine(LogLevel.DEBUG,"[LocalPriceCache:Dispose] End");
instance=null;
if (instance == null || !threadRun) return;
threadRun = false;
threadToJoin = cacheMonitorThread;
cacheMonitorThread = null;
instance = null;
}
if (threadToJoin != null)
{
MDTrace.WriteLine(LogLevel.DEBUG, $"[LocalPriceCache:Dispose] Thread state is '{Utility.ThreadStateToString(threadToJoin)}'. Joining...");
threadToJoin.Join(5000);
}
MDTrace.WriteLine(LogLevel.DEBUG, "[LocalPriceCache:Dispose] End");
}
public static LocalPriceCache GetInstance()
{
lock(typeof(LocalPriceCache))
lock (typeof(LocalPriceCache))
{
if(null==instance)
if (instance == null)
{
instance=new LocalPriceCache();
instance = new LocalPriceCache();
}
return instance;
}
@@ -67,17 +72,17 @@ namespace MarketData.Cache
public void RefreshLatestDate()
{
lock(typeof(LocalPriceCache))
lock (thisLock)
{
latestDate=PricingDA.GetLatestDate();
latestDate = PricingDA.GetLatestDate();
}
}
public DateTime GetLatestDate()
{
lock(typeof(LocalPriceCache))
lock (thisLock)
{
if(Utility.IsEpoch(latestDate))
if (Utility.IsEpoch(latestDate))
{
RefreshLatestDate();
}
@@ -87,65 +92,118 @@ namespace MarketData.Cache
public void Refresh()
{
lock(typeof(LocalPriceCache))
List<string> symbols;
Dictionary<string, DateTime> currentMaxDates;
lock (thisLock)
{
List<String> symbols=new List<String>(priceCache.Keys);
Dictionary<String, DateTime> maxDbDates = PricingDA.GetLatestDates(symbols);
RefreshLatestDate();
foreach(String symbol in symbols)
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>();
DateTime latestDateFromDb;
lock (fetchLock)
{
Dictionary<string, DateTime> maxDbDates = PricingDA.GetLatestDates(symbols);
latestDateFromDb = PricingDA.GetLatestDate();
Parallel.ForEach(symbols, new ParallelOptions { MaxDegreeOfParallelism = 8 }, symbol =>
{
PricesByDate symbolPrices=priceCache[symbol];
DateTime maxDate=symbolPrices.MaxDate; // get the latest date in the cache
if(maxDbDates.ContainsKey(symbol) && !maxDbDates[symbol].Date.Equals(maxDate.Date)) // if the cache date and the database date are not equal then reload the cache
if (!currentMaxDates.TryGetValue(symbol, out var cachedMax)) return;
if (maxDbDates.TryGetValue(symbol, out var dbMax) && dbMax.Date != cachedMax.Date)
{
MDTrace.WriteLine(LogLevel.DEBUG,$"Cache date and Database date for {symbol} are not equal, reloading cache. Cache Date:{maxDate.ToShortDateString()} Database Date:{maxDbDates[symbol].Date.ToShortDateString()}");
Prices prices=PricingDA.GetPrices(symbol,symbolPrices.MinDate); // reload the prices for this symbol using the current minDate in the cache as a lower boundary
if(null==prices)continue; // if we can't load any prices for symbol then just continue
priceCache.Remove(symbol); // remove the pricing entries in the price cache for the symbol
priceCache.Add(symbol,prices.GetPricesByDate()); // reload the cache
Prices prices = PricingDA.GetPrices(symbol, cachedMax);
if (prices != null) fullReloads[symbol] = prices.GetPricesByDate();
}
else
{
MDTrace.WriteLine(LogLevel.DEBUG,$"[LocalPriceCache] Fetching latest price from database for {symbol} on {maxDate.ToShortDateString()}");
Price price=PricingDA.GetPrice(symbol,maxDate); // the max date from the cache equals the max date from the database so just reload the latest price from the database
if(null==price)continue; // if no latest price then just continue
symbolPrices.Remove(maxDate); // remove the current price associated with the max date
symbolPrices.Add(maxDate,price); // reload the latest price for maxDate(symbol) into the cache
{
Price price = PricingDA.GetPrice(symbol, cachedMax);
if (price != null) singleUpdates[symbol] = price;
}
});
}
lock (thisLock)
{
latestDate = latestDateFromDb;
foreach (var kvp in fullReloads)
{
if (priceCache.TryGetValue(kvp.Key, out PricesByDate existing) && existing.MaxDate == currentMaxDates[kvp.Key])
{
priceCache[kvp.Key] = kvp.Value;
}
}
foreach (var kvp in singleUpdates)
{
if (priceCache.TryGetValue(kvp.Key, out PricesByDate pricesByDate) && pricesByDate.MaxDate == currentMaxDates[kvp.Key])
{
// Remove the old price (if any) and add the new price properly
if (pricesByDate.ContainsKey(kvp.Value.Date))
pricesByDate.Remove(kvp.Value.Date);
pricesByDate.Add(kvp.Value.Date, kvp.Value);
}
}
}
MDTrace.WriteLine(LogLevel.DEBUG, $"Full reloads: {fullReloads.Count}, Single updates: {singleUpdates.Count}");
}
// This version of Add(PortfolioTrades) will account for adding multiple lots at different times. So instead of just checking for the existance of the symbol in the cache
// we look to see if the symbol is in the cache and what dates are available. If the date range specified in the trade are not available then we load those date ranges.
// This is a brute force approach always maintaining the gap between successive TradeDates in th.e portfolio trades and the maximum date for the symbol in the database.
// So while it is inefficient in terms of memory usage it alleviates the need for figuring out contiguous price sections
public void Add(PortfolioTrades portfolioTrades)
{
lock(typeof(LocalPriceCache))
List<string> symbols = portfolioTrades.Symbols;
Dictionary<string, DateTime> minTradeDates = symbols.ToDictionary(sym => sym, sym => portfolioTrades.GetMinTradeDate(sym));
Dictionary<string, DateTime> minCacheDates;
lock (thisLock)
{
Profiler profiler=new Profiler();
profiler.Start();
List<String> symbols=portfolioTrades.Symbols;
foreach(String symbol in symbols)
minCacheDates = symbols.ToDictionary(sym => sym, sym => priceCache.ContainsKey(sym) ? priceCache[sym].MinDate : DateTime.MaxValue);
}
ConcurrentDictionary<string, Prices> fetchedPrices = new ConcurrentDictionary<string, Prices>();
Parallel.ForEach(symbols, new ParallelOptions { MaxDegreeOfParallelism = 8 }, symbol =>
{
DateTime minTradeDate = minTradeDates[symbol];
DateTime minCacheDate = minCacheDates[symbol];
Prices prices = null;
try
{
DateTime minPortfolioTradeDate=portfolioTrades.GetMinTradeDate(symbol);
if(!ContainsSymbol(symbol))
if (minCacheDate == DateTime.MaxValue)
{
Prices prices=PricingDA.GetPrices(symbol,minPortfolioTradeDate);
if(null==prices)continue;
foreach(Price price in prices)Add(price);
prices = PricingDA.GetPrices(symbol, minTradeDate);
}
else
else if (minTradeDate < minCacheDate)
{
DateTime minCacheDate=GetMinCacheDate(symbol);
if(minPortfolioTradeDate<minCacheDate)
{
Prices prices=PricingDA.GetPrices(symbol,minCacheDate,minPortfolioTradeDate); // Fill the gap by retrieving prices starting at minCache date and going back in time to minPortfolioTradeDate
if(null==prices)continue;
foreach(Price price in prices)Add(price);
PricesByDate p=priceCache[symbol];
}
prices = PricingDA.GetPrices(symbol, minCacheDate, minTradeDate);
}
if (prices != null && prices.Count > 0)
{
fetchedPrices[symbol] = prices;
}
}
catch (Exception ex)
{
MDTrace.WriteLine(LogLevel.DEBUG, $"Error fetching prices for {symbol}: {ex.Message}");
}
});
lock (thisLock)
{
foreach (var kvp in fetchedPrices)
{
foreach (var price in kvp.Value)
{
Add(price);
}
}
}
@@ -153,131 +211,186 @@ namespace MarketData.Cache
public void Add(Prices prices)
{
foreach(Price price in prices)
{
Add(price);
}
foreach (Price price in prices)
{
Add(price);
}
}
public void Add(List<String> symbols,DateTime pricingDate)
public void Add(List<string> symbols, DateTime pricingDate)
{
foreach(String symbol in symbols)
if (symbols == null || symbols.Count == 0) return;
ConcurrentDictionary<string, Price> fetchedPrices = new ConcurrentDictionary<string, Price>();
Parallel.ForEach(symbols, new ParallelOptions { MaxDegreeOfParallelism = 8 }, symbol =>
{
if(ContainsPrice(symbol,pricingDate))continue;
Price price=PricingDA.GetPrice(symbol,pricingDate);
if(null==price)continue;
Add(price);
lock (thisLock)
{
if (ContainsPrice(symbol, pricingDate)) return;
}
try
{
Price price = PricingDA.GetPrice(symbol, pricingDate);
if (price != null) fetchedPrices[symbol] = price;
}
catch (Exception ex)
{
MDTrace.WriteLine(LogLevel.DEBUG, $"Error fetching price for {symbol} on {pricingDate:yyyy-MM-dd}: {ex.Message}");
}
});
lock (thisLock)
{
foreach (var kvp in fetchedPrices)
{
Add(kvp.Value);
}
}
}
public void Add(Price price)
{
lock(typeof(LocalPriceCache))
if (price == null) return;
lock (thisLock)
{
if(null==price)return;
if(ContainsPrice(price.Symbol,price.Date))return;
PricesByDate pricesByDate=null;
if(!priceCache.ContainsKey(price.Symbol))
if (!priceCache.TryGetValue(price.Symbol, out var pricesByDate))
{
pricesByDate=new PricesByDate();
pricesByDate.Add(price.Date,price);
priceCache.Add(price.Symbol,pricesByDate);
return;
pricesByDate = new PricesByDate();
priceCache[price.Symbol] = pricesByDate;
}
if (!pricesByDate.ContainsKey(price.Date))
{
pricesByDate.Add(price.Date, price); // must use Add() to update MinDate/MaxDate
}
pricesByDate=priceCache[price.Symbol];
if(pricesByDate.ContainsKey(price.Date))return;
pricesByDate.Add(price.Date,price);
}
}
public DateTime GetMinCacheDate(String symbol)
public DateTime GetMinCacheDate(string symbol)
{
if(!ContainsSymbol(symbol))return Utility.Epoch;
PricesByDate symbolPrices=priceCache[symbol];
return symbolPrices.MinDate;
lock (thisLock)
{
if (!priceCache.TryGetValue(symbol, out var symbolPrices) || symbolPrices.Count == 0)
{
return Utility.Epoch;
}
return symbolPrices.MinDate;
}
}
public void RemoveDate(DateTime date)
{
lock(typeof(LocalPriceCache))
lock (thisLock)
{
List<String> symbols=new List<String>(priceCache.Keys);
foreach(String key in symbols)
foreach (var kvp in priceCache)
{
PricesByDate pricesByDate=priceCache[key];
if(pricesByDate.ContainsKey(date))pricesByDate.Remove(date);
kvp.Value.Remove(date);
}
}
}
public Price GetPrice(String symbol,DateTime date)
{
lock(typeof(LocalPriceCache))
{
if(!priceCache.ContainsKey(symbol))return null;
PricesByDate pricesByDate=priceCache[symbol];
if(!pricesByDate.ContainsKey(date))return null;
return pricesByDate[date];
}
}
public bool ContainsPrice(String symbol,DateTime date)
{
lock(typeof(LocalPriceCache))
{
if(!priceCache.ContainsKey(symbol))return false;
PricesByDate pricesByDate=priceCache[symbol];
if(!pricesByDate.ContainsKey(date))return false;
return true;
}
}
public bool ContainsPrice(List<String> symbols,DateTime date)
{
lock(typeof(LocalPriceCache))
{
foreach(String symbol in symbols)if(!ContainsPrice(symbol,date))return false;
return true;
}
}
public bool ContainsSymbol(String symbol)
public Prices GetPrices(string symbol, DateTime endDate, int dayCount)
{
lock(typeof(LocalPriceCache))
lock (thisLock)
{
if(priceCache.ContainsKey(symbol))return true;
return false;
if (!priceCache.TryGetValue(symbol, out var 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 long Count()
public Price GetPrice(string symbol, DateTime date)
{
long count=0;
List<String> symbols=priceCache.Keys.ToList();
foreach(String symbol in symbols)
lock (thisLock)
{
PricesByDate pricesByDate=priceCache[symbol];
count+=pricesByDate.Count;
if (!priceCache.TryGetValue(symbol, out var pricesByDate)) return null;
return pricesByDate.TryGetValue(date, out var price) ? price : null;
}
}
public bool ContainsPrice(string symbol, DateTime date)
{
lock (thisLock)
{
if (!priceCache.TryGetValue(symbol, out var pricesByDate)) return false;
return pricesByDate.ContainsKey(date);
}
}
public bool ContainsPrice(List<string> symbols, DateTime date)
{
if (symbols == null || symbols.Count == 0) return false;
lock (thisLock)
{
foreach (string symbol in symbols)
{
if (!priceCache.TryGetValue(symbol, out var pricesByDate) || !pricesByDate.ContainsKey(date))
{
return false;
}
}
return true;
}
}
public bool ContainsSymbol(string symbol)
{
lock (thisLock)
{
return priceCache.ContainsKey(symbol);
}
}
private long Count()
{
lock (thisLock)
{
long count = 0;
foreach (var pricesByDate in priceCache.Values)
{
count += pricesByDate.Count;
}
return count;
}
return count;
}
private void ThreadProc()
{
int quantums=0;
int quantumInterval=1000;
long lastCount=0;
while(threadRun)
int quantums = 0;
int quantumInterval = 1000;
long lastCount = 0;
while (threadRun)
{
Thread.Sleep(quantumInterval);
quantums+=quantumInterval;
if(quantums>cacheCycle)
quantums += quantumInterval;
if (quantums > cacheCycle)
{
quantums=0;
lock(thisLock)
quantums = 0;
lock (thisLock)
{
lastCount=Count();
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[LocalPriceCache:ThreadProc] Symbols: {0}. Items in cache: {1}.",priceCache.Keys.Count,Utility.FormatNumber(lastCount,0,true)));
lastCount = Count();
MDTrace.WriteLine(LogLevel.DEBUG, $"[LocalPriceCache:ThreadProc] Symbols: {priceCache.Keys.Count}. Items in cache: {Utility.FormatNumber(lastCount,0,true)}.");
}
}
}
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[LocalPriceCache:ThreadProc] Thread ended. Items in cache:{0}",Utility.FormatNumber(lastCount,0,true)));
MDTrace.WriteLine(LogLevel.DEBUG, $"[LocalPriceCache:ThreadProc] Thread ended. Items in cache:{Utility.FormatNumber(lastCount,0,true)}");
}
}
}
}

View File

@@ -146,8 +146,9 @@ namespace MarketData.Generator.CMMomentum
price=PricingDA.GetPrice(openPosition.Symbol,currentDate);
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString()));
return null;
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("CMBacktest::GetModelPerformance No price for {0} on {1}",openPosition.Symbol,currentDate.ToShortDateString()));
continue;
// return null;
}
LocalPriceCache.GetInstance().Add(price);
}

View File

@@ -150,7 +150,8 @@ namespace MarketData.Generator.CMTrend
if(null==price)
{
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("******************* No price for {0} on {1} *****************",openPosition.Symbol,currentDate.ToShortDateString()));
return performanceSeries;
continue;
// return performanceSeries;
}
gainLoss+=((price.Close*openPosition.Shares)-(openPosition.PurchasePrice*openPosition.Shares));
marketValue+=(price.Close*openPosition.Shares);

View File

@@ -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)

View File

@@ -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();

View File

@@ -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()));

View File

@@ -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)));

View File

@@ -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" />

View File

@@ -1857,6 +1857,7 @@ namespace MarketData
{
LocalPriceCache.GetInstance().Dispose();
GBPriceCache.GetInstance().Dispose();
GLPriceCache.GetInstance().Dispose();
}
} // main
// *****************************************************************************************************************************************************************************

View File

@@ -1,23 +1,22 @@
CMSESSIONv1.00
LastUpdated=2/2/2026 10:48:05 AM
TradeDate=2/27/2026
LastUpdated=3/2/2026 11:30:34 AM
TradeDate=3/31/2026
StartDate=10/31/2019
AnalysisDate=2/2/2026
Cycle=76
CashBalance=1871.45
AnalysisDate=3/2/2026
Cycle=77
CashBalance=7632.48
NonTradeableCash=13400.66
DayCount=90|AnalysisDate=2/2/2026|TradeDate=2/27/2026|DailyReturnLimit=0.15|MovingAverageConstraintDays=100|FallbackCandidateBestOf=SHV,NEAR,BIL,GSY,AGG,ACWX,GSY,SCHF,IXUS,DBEF,IEFA,TLT|Benchmark=SPY|BenchmarkMovingAverageDays=200|HoldingPeriod=3|MaxPositions=3|NoTradeSymbols=SFTBY,IPCC,YPF,ALGT,CXO,EE,APLP,SE,GBTC,YOKU,PNY,RFMD,ASAZY|InitialCash=5000|TargetBeta=1|BetaMonths=6|MarketCapLowerLimit=1000000000|MaxBeta=10|UseMaxBeta=False|FallbackMaxAlloc=1000|UseOverExtendedIndicator=True|UseOverExtendedIndicatorDays=10|UseOverExtendedIndicatorViolationThreshhold=1|UseOverExtendedIndicatorMarginPercent=1|UseMaxPositionBucketWeight=True|UseMaxPositionBucketWeightMaxWeight=0.65|UseCNN=True|UseCNNHost=http://Euporie:5000|UseCNNDayCount=270|UseCNNRewardPercentDecimal=0.25
TotalActivePositions=9
DayCount=90|AnalysisDate=3/2/2026|TradeDate=3/31/2026|DailyReturnLimit=0.15|MovingAverageConstraintDays=100|FallbackCandidateBestOf=SHV,NEAR,BIL,GSY,AGG,ACWX,GSY,SCHF,IXUS,DBEF,IEFA,TLT|Benchmark=SPY|BenchmarkMovingAverageDays=200|HoldingPeriod=3|MaxPositions=3|NoTradeSymbols=SFTBY,IPCC,YPF,ALGT,CXO,EE,APLP,SE,GBTC,YOKU,PNY,RFMD,ASAZY|InitialCash=5000|TargetBeta=1|BetaMonths=6|MarketCapLowerLimit=1000000000|MaxBeta=10|UseMaxBeta=False|FallbackMaxAlloc=1000|UseOverExtendedIndicator=True|UseOverExtendedIndicatorDays=10|UseOverExtendedIndicatorViolationThreshhold=1|UseOverExtendedIndicatorMarginPercent=1|UseMaxPositionBucketWeight=True|UseMaxPositionBucketWeightMaxWeight=0.65|UseCNN=True|UseCNNHost=http://Euporie:5000|UseCNNDayCount=270|UseCNNRewardPercentDecimal=0.25
TotalActivePositions=8
Slot=0|Symbol=AA|PurchaseDate=1/30/2026 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=106|PurchasePrice=56.81|Beta=1.38742001868201|BetaMonths=6|SharpeRatio=0.042662472208833|RiskAdjustedWeight=0.487240585470048|RiskAdjustedAllocation=6029.38948346953|TargetBetaOverBeta=0.720762268480138|Score=0|CNNPrediction=True
Slot=0|Symbol=TGB|PurchaseDate=1/30/2026 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=221|PurchasePrice=7.34|Beta=4.95425362268808|BetaMonths=6|SharpeRatio=0.463380326422422|RiskAdjustedWeight=0.136449886033226|RiskAdjustedAllocation=1688.50775654428|TargetBetaOverBeta=0.201846751530944|Score=0|CNNPrediction=True
Slot=0|Symbol=BORR|PurchaseDate=1/30/2026 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=995|PurchasePrice=4.54|Beta=1.79641303502463|BetaMonths=6|SharpeRatio=-0.0951973473446902|RiskAdjustedWeight=0.376309528496725|RiskAdjustedAllocation=4656.66609331953|TargetBetaOverBeta=0.556664854074771|Score=0|CNNPrediction=True
Slot=1|Symbol=MU|PurchaseDate=11/28/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=7|PurchasePrice=232.72|Beta=8.49114666107215|BetaMonths=6|SharpeRatio=0.247081001353389|RiskAdjustedWeight=0.14264664163143|RiskAdjustedAllocation=1664.32208341382|TargetBetaOverBeta=0.117769724150982|Score=0|CNNPrediction=True
Slot=1|Symbol=TTI|PurchaseDate=11/28/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=490|PurchasePrice=7.72|Beta=3.71100734182263|BetaMonths=6|SharpeRatio=0.248087502251472|RiskAdjustedWeight=0.326389425628832|RiskAdjustedAllocation=3808.13121608837|TargetBetaOverBeta=0.26946861266754|Score=0|CNNPrediction=True
Slot=1|Symbol=NESR|PurchaseDate=11/28/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=444|PurchasePrice=14|Beta=2.28119742249155|BetaMonths=6|SharpeRatio=-0.00800623634647377|RiskAdjustedWeight=0.530963932739738|RiskAdjustedAllocation=6194.99336716448|TargetBetaOverBeta=0.438366267706802|Score=0|CNNPrediction=True
Slot=2|Symbol=RAPT|PurchaseDate=12/31/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=38|PurchasePrice=33.93|Beta=28.6944470511615|BetaMonths=6|SharpeRatio=0.353702556914886|RiskAdjustedWeight=0.0470862209044434|RiskAdjustedAllocation=532.873977205238|TargetBetaOverBeta=0.034849948431382|Score=0|CNNPrediction=True
Slot=1|Symbol=MU|PurchaseDate=2/27/2026 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=4|PurchasePrice=401.9|Beta=8.75628950815446|BetaMonths=6|SharpeRatio=0.576855333922295|RiskAdjustedWeight=0.112389472785322|RiskAdjustedAllocation=1666.58190782861|TargetBetaOverBeta=0.114203624613911|Score=0|CNNPrediction=False
Slot=1|Symbol=CENX|PurchaseDate=2/27/2026 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=92|PurchasePrice=52.07|Beta=3.07146309580131|BetaMonths=6|SharpeRatio=0.367390034814581|RiskAdjustedWeight=0.320405855672632|RiskAdjustedAllocation=4751.17988360287|TargetBetaOverBeta=0.325577735694432|Score=0|CNNPrediction=True
Slot=1|Symbol=BVN|PurchaseDate=2/27/2026 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=192|PurchasePrice=43.5|Beta=1.73502583944106|BetaMonths=6|SharpeRatio=0.439689953082546|RiskAdjustedWeight=0.567204671542045|RiskAdjustedAllocation=8410.86820856852|TargetBetaOverBeta=0.576360292318268|Score=0|CNNPrediction=True
Slot=2|Symbol=HL|PurchaseDate=12/31/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=379|PurchasePrice=19.65|Beta=1.71757923481307|BetaMonths=6|SharpeRatio=0.383963559078681|RiskAdjustedWeight=0.786637987463148|RiskAdjustedAllocation=8902.36899348732|TargetBetaOverBeta=0.582214770492865|Score=0|CNNPrediction=True
Slot=2|Symbol=SPHR|PurchaseDate=12/31/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=27|PurchasePrice=94.69|Beta=8.12573531791566|BetaMonths=6|SharpeRatio=0.196920851690333|RiskAdjustedWeight=0.166275791632408|RiskAdjustedAllocation=1881.74036264077|TargetBetaOverBeta=0.123065785541303|Score=0|CNNPrediction=True
TotalPositions=187
TotalPositions=191
Symbol=CLDR|PurchaseDate=10/31/2019 12:00:00 AM|SellDate=1/31/2020 12:00:00 AM|Shares=124|PurchasePrice=8.56|Beta=0.482592987013072|BetaMonths=36|SharpeRatio=-0.248675615247893|RiskAdjustedWeight=0.632894313054559|RiskAdjustedAllocation=1054.82385509093|TargetBetaOverBeta=2.07213951903721|CurrentPrice=10.36|Score=0|CNNPrediction=False
Symbol=RH|PurchaseDate=10/31/2019 12:00:00 AM|SellDate=1/31/2020 12:00:00 AM|Shares=2|PurchasePrice=181.92|Beta=1.3764249979282|BetaMonths=36|SharpeRatio=0.124651578968104|RiskAdjustedWeight=0.221901198728824|RiskAdjustedAllocation=369.835331214706|TargetBetaOverBeta=0.726519789676302|CurrentPrice=210.25|Score=0|CNNPrediction=False
Symbol=VC|PurchaseDate=10/31/2019 12:00:00 AM|SellDate=1/31/2020 12:00:00 AM|Shares=2|PurchasePrice=93.22|Beta=2.10344983651568|BetaMonths=36|SharpeRatio=-0.0621291508319681|RiskAdjustedWeight=0.145204488216617|RiskAdjustedAllocation=242.007480361029|TargetBetaOverBeta=0.475409483335472|CurrentPrice=80.34|Score=0|CNNPrediction=False
@@ -205,3 +204,7 @@ Symbol=BIL|PurchaseDate=9/30/2025 12:00:00 AM|SellDate=12/31/2025 12:00:00 AM|Sh
Symbol=UEC|PurchaseDate=10/31/2025 12:00:00 AM|SellDate=1/30/2026 12:00:00 AM|Shares=143|PurchasePrice=15|Beta=1.29663058800792|BetaMonths=6|SharpeRatio=0.188824194390374|RiskAdjustedWeight=0.104687628773719|RiskAdjustedAllocation=1106.03003237578|TargetBetaOverBeta=0.771229685038012|CurrentPrice=17.41|Score=0|CNNPrediction=True
Symbol=BTU|PurchaseDate=10/31/2025 12:00:00 AM|SellDate=1/30/2026 12:00:00 AM|Shares=249|PurchasePrice=28.22|Beta=0.159849444660176|BetaMonths=6|SharpeRatio=-0.0527083016848311|RiskAdjustedWeight=0.849181440339659|RiskAdjustedAllocation=8971.64437626051|TargetBetaOverBeta=6.2558866070877|CurrentPrice=34.75|Score=0|CNNPrediction=True
Symbol=WDC|PurchaseDate=10/31/2025 12:00:00 AM|SellDate=1/30/2026 12:00:00 AM|Shares=10|PurchasePrice=152.3|Beta=2.94251988947805|BetaMonths=6|SharpeRatio=0.169350251432453|RiskAdjustedWeight=0.046130930886622|RiskAdjustedAllocation=487.375591363706|TargetBetaOverBeta=0.339844771678801|CurrentPrice=243.52|Score=0|CNNPrediction=True
Symbol=RAPT|PurchaseDate=12/31/2025 12:00:00 AM|SellDate=2/18/2026 12:00:00 AM|Shares=38|PurchasePrice=33.93|Beta=28.6944470511615|BetaMonths=6|SharpeRatio=0.353702556914886|RiskAdjustedWeight=0.0470862209044434|RiskAdjustedAllocation=532.873977205238|TargetBetaOverBeta=0.034849948431382|CurrentPrice=57.815|Score=0|CNNPrediction=True
Symbol=MU|PurchaseDate=11/28/2025 12:00:00 AM|SellDate=2/27/2026 12:00:00 AM|Shares=7|PurchasePrice=232.72|Beta=8.49114666107215|BetaMonths=6|SharpeRatio=0.247081001353389|RiskAdjustedWeight=0.14264664163143|RiskAdjustedAllocation=1664.32208341382|TargetBetaOverBeta=0.117769724150982|CurrentPrice=401.9|Score=0|CNNPrediction=True
Symbol=TTI|PurchaseDate=11/28/2025 12:00:00 AM|SellDate=2/27/2026 12:00:00 AM|Shares=490|PurchasePrice=7.72|Beta=3.71100734182263|BetaMonths=6|SharpeRatio=0.248087502251472|RiskAdjustedWeight=0.326389425628832|RiskAdjustedAllocation=3808.13121608837|TargetBetaOverBeta=0.26946861266754|CurrentPrice=8.8|Score=0|CNNPrediction=True
Symbol=NESR|PurchaseDate=11/28/2025 12:00:00 AM|SellDate=2/27/2026 12:00:00 AM|Shares=444|PurchasePrice=14|Beta=2.28119742249155|BetaMonths=6|SharpeRatio=-0.00800623634647377|RiskAdjustedWeight=0.530963932739738|RiskAdjustedAllocation=6194.99336716448|TargetBetaOverBeta=0.438366267706802|CurrentPrice=25.2|Score=0|CNNPrediction=True

View File

@@ -1,16 +1,15 @@
CMTSESSIONv1.00
LastUpdated=2/13/2026 8:56:35 PM
TradeDate=2/13/2026
LastUpdated=3/10/2026 9:30:20 PM
TradeDate=3/10/2026
StartDate=1/1/0001
AnalysisDate=2/13/2026
CashBalance=4287.32
AnalysisDate=3/10/2026
CashBalance=6863.35
NonTradeableCash=6456.42
SuspendTrading=False|UsePriceSlopeIndicator=True|UsePriceSlopeIndicatorDays=252|AnalysisDate=2/13/2026|BetaMonths=6|TradeDate=2/13/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=3/10/2026|BetaMonths=6|TradeDate=3/10/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=250.21|Exposure=2208.91|MarketValue=2752.31|GainLoss=543.4|GainLossPcnt=0.246003685075445|PositionRiskDecimal=0.12|R=24.0336|C=276.15|P=11.490163770721|InitialStopLimit=176.71|TrailingStopLimit=197.56385799408|TotalRiskExposure=264.3696|RMultiple=2.06R|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=61.36|Exposure=1772.43|MarketValue=2515.76|GainLoss=743.33|GainLossPcnt=0.419384686560259|PositionRiskDecimal=0.12|R=5.2188|C=218.4595|P=41.8601019391431|InitialStopLimit=38.04|TrailingStopLimit=50.7683570289612|TotalRiskExposure=213.9708|RMultiple=3.47R|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
TotalPositions=133
TotalActivePositions=1
Symbol=HWM|PurchaseDate=11/17/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=11|PurchasePrice=200.81|CurrentPrice=253.91|Exposure=2208.91|MarketValue=2793.01|GainLoss=584.1|GainLossPcnt=0.264429062297694|PositionRiskDecimal=0.12|R=24.0336|C=276.15|P=11.490163770721|InitialStopLimit=176.71|TrailingStopLimit=235.233001537323|TotalRiskExposure=264.3696|RMultiple=2.21R|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
TotalPositions=134
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.
Symbol=POOL|PurchaseDate=9/1/2020 12:00:00 AM|SellDate=9/8/2020 12:00:00 AM|Shares=2|PurchasePrice=332.21|CurrentPrice=288.44|Exposure=664.42|MarketValue=576.88|GainLoss=-87.54|GainLossPcnt=-0.131754011017128|PositionRiskDecimal=0.12|R=39.5904|C=80.0065|P=2.02085606611704|InitialStopLimit=290.3296|TrailingStopLimit=290.3296|TotalRiskExposure=79.1808|RMultiple=-1.11R|Volatility=4.28818368911743|Volume=259404|LastStopAdjustment=1/1/0001 12:00:00 AM|Comment=Manual close.
@@ -144,20 +143,18 @@ 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=120
Symbol=FTI|PurchaseDate=11/20/2025 12:00:00 AM|SellDate=3/6/2026 12:00:00 AM|Shares=41|PurchasePrice=43.23|CurrentPrice=62.83|Exposure=1772.43|MarketValue=2576.03|GainLoss=803.6|GainLossPcnt=0.453388850335415|PositionRiskDecimal=0.12|R=5.2188|C=218.4595|P=41.8601019391431|InitialStopLimit=38.04|TrailingStopLimit=62.8357140827179|TotalRiskExposure=213.9708|RMultiple=3.76R|Volatility=0.825829267501831|Volume=0|LastStopAdjustment=2/25/2026 12:00:00 AM|Comment=Manual close.
TotalCandidates=126
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
Symbol=SBSW|AnalysisDate=9/25/2025 12:00:00 AM|EPSSlope=0.144999995827675|ProfitMarginSlope=8.49569129943848|PriceSlope=0.00379709174022612|Volatility=0.753211140632629|Volume=0|Violation=False|Slope=0.00379709174022612|Score=1.72251869189193|AnnualizedReturn=2.60352714141071|SharpeRatio=0.241739220317797|RSquared=0.661609654262558|BetaMonths=6|Beta=1.87093907966492
Symbol=NTES|AnalysisDate=9/15/2025 12:00:00 AM|EPSSlope=0.204999923706055|ProfitMarginSlope=1.94379615783691|PriceSlope=0.00231223761471691|Volatility=7.26750755310059|Volume=0|Violation=False|Slope=0.00231223761471691|Score=1.55371831188788|AnnualizedReturn=1.79083837998442|SharpeRatio=0.0978165524611287|RSquared=0.867592703648334|BetaMonths=6|Beta=1.22658293677365
Symbol=SANM|AnalysisDate=10/6/2025 12:00:00 AM|EPSSlope=0.174999952316284|ProfitMarginSlope=0.24951171875|PriceSlope=0.00208239218708193|Volatility=6.52257966995239|Volume=0|Violation=False|Slope=0.00208239218708193|Score=1.2404476041886|AnnualizedReturn=1.6900579717286|SharpeRatio=0.133157857131703|RSquared=0.733967488061884|BetaMonths=6|Beta=1.12080932540038
Symbol=VSEC|AnalysisDate=8/18/2025 12:00:00 AM|EPSSlope=0.479999989271164|ProfitMarginSlope=0.627522468566895|PriceSlope=0.00197968074908862|Volatility=7.79957246780396|Volume=0|Violation=False|Slope=0.00197968074908862|Score=1.23555493984458|AnnualizedReturn=1.64687499344999|SharpeRatio=0.0848111495425649|RSquared=0.750242091694071|BetaMonths=6|Beta=0.00511743208922735
Symbol=APH|AnalysisDate=9/10/2025 12:00:00 AM|EPSSlope=0.225000023841858|ProfitMarginSlope=1.02336883544922|PriceSlope=0.00218577417499694|Volatility=3.01760697364807|Volume=0|Violation=False|Slope=0.00218577417499694|Score=1.19871329173408|AnnualizedReturn=1.73466635462984|SharpeRatio=0.117857316723347|RSquared=0.691033920462406|BetaMonths=6|Beta=1.29660077545655
Symbol=BVN|AnalysisDate=9/22/2025 12:00:00 AM|EPSSlope=0.0400000214576721|ProfitMarginSlope=3.35278701782227|PriceSlope=0.00178832525596272|Volatility=0.509544312953949|Volume=0|Violation=False|Slope=0.00178832525596272|Score=1.1871859154563|AnnualizedReturn=1.56934441878645|SharpeRatio=0.0627467548412755|RSquared=0.756485256674465|BetaMonths=6|Beta=0.0852063395469229
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
@@ -168,28 +165,20 @@ Symbol=NVMI|AnalysisDate=9/30/2025 12:00:00 AM|EPSSlope=0.365000009536743|Profit
Symbol=PTCT|AnalysisDate=9/11/2025 12:00:00 AM|EPSSlope=0.229999780654907|ProfitMarginSlope=0.267559051513672|PriceSlope=0.00114877088928268|Volatility=4.3342490196228|Volume=0|Violation=False|Slope=0.00114877088928268|Score=0.606345820774638|AnnualizedReturn=1.33574643654898|SharpeRatio=0.0125861708714166|RSquared=0.453937816477493|BetaMonths=6|Beta=0.276668916101194
Symbol=LGND|AnalysisDate=9/30/2025 12:00:00 AM|EPSSlope=1.55999994277954|ProfitMarginSlope=0.261482238769531|PriceSlope=0.00131115840818318|Volatility=3.33523750305176|Volume=0|Violation=False|Slope=0.00131115840818318|Score=0.566445116927072|AnnualizedReturn=1.3915412124965|SharpeRatio=0.0375984436300414|RSquared=0.407063126726114|BetaMonths=6|Beta=0.429603430352865
Symbol=PRIM|AnalysisDate=9/19/2025 12:00:00 AM|EPSSlope=0.315000057220459|ProfitMarginSlope=0.827674865722656|PriceSlope=0.00174915986971411|Volatility=4.4852933883667|Volume=0|Violation=False|Slope=0.00174915986971411|Score=0.560411044122823|AnnualizedReturn=1.55393168023759|SharpeRatio=0.261359856273027|RSquared=0.360640722658502|BetaMonths=6|Beta=2.63821547738842
Symbol=GLW|AnalysisDate=9/5/2025 12:00:00 AM|EPSSlope=0.21000000834465|ProfitMarginSlope=0.926633834838867|PriceSlope=0.0010950711054805|Volatility=2.05026125907898|Volume=0|Violation=False|Slope=0.0010950711054805|Score=0.557801280206709|AnnualizedReturn=1.31779240828614|SharpeRatio=0.0647764056434674|RSquared=0.423284636259333|BetaMonths=6|Beta=1.72198299721121
Symbol=BWXT|AnalysisDate=10/1/2025 12:00:00 AM|EPSSlope=0.0299999713897705|ProfitMarginSlope=0.842325210571289|PriceSlope=0.00148142098327814|Volatility=5.46874237060547|Volume=0|Violation=False|Slope=0.00148142098327814|Score=0.535519927511107|AnnualizedReturn=1.45254630359425|SharpeRatio=0.0847391180072971|RSquared=0.368676665374447|BetaMonths=6|Beta=1.20408002256826
Symbol=MU|AnalysisDate=10/16/2025 12:00:00 AM|EPSSlope=1.01999998092651|ProfitMarginSlope=3.93631362915039|PriceSlope=0.0017836395604108|Volatility=12.5083780288696|Volume=0|Violation=False|Slope=0.0017836395604108|Score=0.535470880696113|AnnualizedReturn=1.56749243792573|SharpeRatio=0.131165993959009|RSquared=0.341609865374982|BetaMonths=6|Beta=3.80362957802949
Symbol=COLL|AnalysisDate=11/17/2025 12:00:00 AM|EPSSlope=0.285000026226044|ProfitMarginSlope=3.47329521179199|PriceSlope=0.000996351284585738|Volatility=4.86669015884399|Volume=0|Violation=False|Slope=0.000996351284585738|Score=0.498972314260969|AnnualizedReturn=1.28541358644203|SharpeRatio=-0.244881207463219|RSquared=0.388180364299792|BetaMonths=6|Beta=1.46108876190814
Symbol=BELFB|AnalysisDate=8/25/2025 12:00:00 AM|EPSSlope=0.285000085830688|ProfitMarginSlope=0.582233428955078|PriceSlope=0.00141653955297341|Volatility=2.603111743927|Volume=0|Violation=False|Slope=0.00141653955297341|Score=0.480716598993722|AnnualizedReturn=1.4289900947802|SharpeRatio=0.119774159326474|RSquared=0.336403030888512|BetaMonths=6|Beta=2.43681291066381
Symbol=GSL|AnalysisDate=9/18/2025 12:00:00 AM|EPSSlope=0.0900001525878906|ProfitMarginSlope=0.176097869873047|PriceSlope=0.00102036281428555|Volatility=0.917586147785187|Volume=0|Violation=False|Slope=0.00102036281428555|Score=0.447266351637514|AnnualizedReturn=1.29321508181417|SharpeRatio=-0.240771308468293|RSquared=0.345856120862797|BetaMonths=6|Beta=0.967212765650389
Symbol=CGAU|AnalysisDate=9/25/2025 12:00:00 AM|EPSSlope=0.0750000029802322|ProfitMarginSlope=7.88293075561523|PriceSlope=0.0009970382360156|Volatility=0.387202262878418|Volume=0|Violation=False|Slope=0.0009970382360156|Score=0.429807798377358|AnnualizedReturn=1.28563612591224|SharpeRatio=-0.153030522888472|RSquared=0.334315277639216|BetaMonths=6|Beta=0.89521003942289
Symbol=MD|AnalysisDate=11/17/2025 12:00:00 AM|EPSSlope=0.300000011920929|ProfitMarginSlope=3.04110622406006|PriceSlope=0.000999694216261977|Volatility=2.24461984634399|Volume=0|Violation=False|Slope=0.000999694216261977|Score=0.412248257927109|AnnualizedReturn=1.28649689922789|SharpeRatio=-0.0107905850378413|RSquared=0.320442480797681|BetaMonths=6|Beta=3.52283407368269
Symbol=BTG|AnalysisDate=9/2/2025 12:00:00 AM|EPSSlope=0.0600000023841858|ProfitMarginSlope=7.98205184936523|PriceSlope=0.00108754638880565|Volatility=0.141802281141281|Volume=0|Violation=False|Slope=0.00108754638880565|Score=0.403083945684378|AnnualizedReturn=1.31529594031496|SharpeRatio=0.0341477983681376|RSquared=0.30645874690973|BetaMonths=6|Beta=0.468022424186877
Symbol=THC|AnalysisDate=9/26/2025 12:00:00 AM|EPSSlope=0.25|ProfitMarginSlope=0.327152252197266|PriceSlope=0.000955274874018335|Volatility=5.08288431167603|Volume=0|Violation=False|Slope=0.000955274874018335|Score=0.322936552181079|AnnualizedReturn=1.2721765700957|SharpeRatio=-0.209542335356885|RSquared=0.25384570017414|BetaMonths=6|Beta=1.46034995404185
Symbol=GSAT|AnalysisDate=11/12/2025 12:00:00 AM|EPSSlope=0.115000009536743|ProfitMarginSlope=1.31202697753906|PriceSlope=0.00171636000994749|Volatility=6.80252933502197|Volume=0|Violation=False|Slope=0.00171636000994749|Score=0.318381945542454|AnnualizedReturn=1.54114049347012|SharpeRatio=0.26987857374401|RSquared=0.206588527711427|BetaMonths=6|Beta=2.29067093580777
Symbol=AIR|AnalysisDate=9/29/2025 12:00:00 AM|EPSSlope=0.350000023841858|ProfitMarginSlope=0.581799507141113|PriceSlope=0.000698221037833735|Volatility=4.6066722869873|Volume=0|Violation=False|Slope=0.000698221037833735|Score=0.253698463892993|AnnualizedReturn=1.19238046711253|SharpeRatio=-0.225066718117313|RSquared=0.212766370206775|BetaMonths=6|Beta=2.52704300010446
Symbol=LVS|AnalysisDate=11/20/2025 12:00:00 AM|EPSSlope=0.120000004768372|ProfitMarginSlope=0.147016525268555|PriceSlope=0.00096485189417096|Volatility=2.16808557510376|Volume=0|Violation=False|Slope=0.00096485189417096|Score=0.241004703156408|AnnualizedReturn=1.27525056048546|SharpeRatio=-0.198411497252607|RSquared=0.188986157406323|BetaMonths=6|Beta=1.51109611853874
Symbol=HBM|AnalysisDate=9/3/2025 12:00:00 AM|EPSSlope=0.175000011920929|ProfitMarginSlope=0.490240097045898|PriceSlope=0.000729030817168273|Volatility=0.487358808517456|Volume=0|Violation=False|Slope=0.000729030817168273|Score=0.175331906747473|AnnualizedReturn=1.20167421779262|SharpeRatio=0.0685094425688964|RSquared=0.145906356441219|BetaMonths=6|Beta=2.69015248129092
Symbol=BILI|AnalysisDate=9/15/2025 12:00:00 AM|EPSSlope=0.135000005364418|ProfitMarginSlope=1.50113868713379|PriceSlope=0.000573695565703754|Volatility=1.41854751110077|Volume=0|Violation=False|Slope=0.000573695565703754|Score=0.108280835698724|AnnualizedReturn=1.1555440621639|SharpeRatio=0.067556996898538|RSquared=0.0937055013687271|BetaMonths=6|Beta=0.725243490158731
Symbol=MCY|AnalysisDate=10/3/2025 12:00:00 AM|EPSSlope=0.934999942779541|ProfitMarginSlope=2.7081880569458|PriceSlope=0.000598694193818475|Volatility=2.73796057701111|Volume=0|Violation=False|Slope=0.000598694193818475|Score=0.0983982079050049|AnnualizedReturn=1.16284656774647|SharpeRatio=-0.0693450586498807|RSquared=0.0846183930315889|BetaMonths=6|Beta=1.50736316396258
Symbol=VITL|AnalysisDate=8/22/2025 12:00:00 AM|EPSSlope=0.00499999523162842|ProfitMarginSlope=1.38887023925781|PriceSlope=0.000455363083461419|Volatility=4.98714113235474|Volume=0|Violation=False|Slope=0.000455363083461419|Score=0.083100854866897|AnnualizedReturn=1.12159468333037|SharpeRatio=-0.244814541455398|RSquared=0.0740916982774419|BetaMonths=6|Beta=1.21500191244139
Symbol=CSIQ|AnalysisDate=10/31/2025 12:00:00 AM|EPSSlope=0.0249999985098839|ProfitMarginSlope=7.77392435073853|PriceSlope=0.000623090718563301|Volatility=1.79284286499023|Volume=0|Violation=False|Slope=0.000623090718563301|Score=0.0807088026857748|AnnualizedReturn=1.1700176814875|SharpeRatio=-0.187760717880484|RSquared=0.0689808401725739|BetaMonths=6|Beta=1.33520035507038
Symbol=MUX|AnalysisDate=9/2/2025 12:00:00 AM|EPSSlope=0.155000001192093|ProfitMarginSlope=12.6074028015137|PriceSlope=0.000420282872695496|Volatility=0.892517030239105|Volume=0|Violation=False|Slope=0.000420282872695496|Score=0.0505830547931327|AnnualizedReturn=1.11172324440238|SharpeRatio=0.0664507478013526|RSquared=0.0454996826303873|BetaMonths=6|Beta=2.4677990769044
Symbol=TV|AnalysisDate=9/5/2025 12:00:00 AM|EPSSlope=0.0250000059604645|ProfitMarginSlope=3.05452346801758|PriceSlope=0.000362561864863969|Volatility=0.196487531065941|Volume=0|Violation=False|Slope=0.000362561864863969|Score=0.0409861983117743|AnnualizedReturn=1.09566949779821|SharpeRatio=0.0530850799580673|RSquared=0.0374074466745106|BetaMonths=6|Beta=2.16132791096815
Symbol=IMOS|AnalysisDate=11/18/2025 12:00:00 AM|EPSSlope=0.0399999991059303|ProfitMarginSlope=1.49780511856079|PriceSlope=0.00022021307999562|Volatility=3.45910954475403|Volume=0|Violation=False|Slope=0.00022021307999562|Score=0.0232785327570433|AnnualizedReturn=1.05706235349833|SharpeRatio=-0.325433209217528|RSquared=0.0220219107037569|BetaMonths=6|Beta=2.37560627494608
Symbol=HL|AnalysisDate=8/29/2025 12:00:00 AM|EPSSlope=0.0249999985098839|ProfitMarginSlope=5.97112083435059|PriceSlope=0.000112741516887054|Volatility=0.264708667993546|Volume=0|Violation=False|Slope=0.000112741516887054|Score=0.00468681348236116|AnnualizedReturn=1.02881830020441|SharpeRatio=-0.341675964720262|RSquared=0.00455553082738707|BetaMonths=6|Beta=0.630190379108049
Symbol=IRMD|AnalysisDate=11/21/2025 12:00:00 AM|EPSSlope=0.0199999809265137|ProfitMarginSlope=0.859756469726563|PriceSlope=0.0017007269483326|Volatility=1.49323868751526|Volume=0|Violation=False|Slope=0.0017007269483326|Score=1.00958866945961|AnnualizedReturn=1.5350810653994|SharpeRatio=0.131721204689824|RSquared=0.657677755406961|BetaMonths=6|Beta=0.443438397302597
Symbol=FTI|AnalysisDate=11/21/2025 12:00:00 AM|EPSSlope=0.0600000619888306|ProfitMarginSlope=0.997105598449707|PriceSlope=0.00151512058909537|Volatility=0.719519555568695|Volume=0|Violation=False|Slope=0.00151512058909537|Score=0.893034560876287|AnnualizedReturn=1.46493429033877|SharpeRatio=0.0408456480409023|RSquared=0.609607247755647|BetaMonths=6|Beta=2.43194338962535
Symbol=COCO|AnalysisDate=11/24/2025 12:00:00 AM|EPSSlope=0.0399999618530273|ProfitMarginSlope=0.468318939208984|PriceSlope=0.000702689165827794|Volatility=3.09965920448303|Volume=0|Violation=False|Slope=0.000702689165827794|Score=0.301275890192158|AnnualizedReturn=1.19372380580228|SharpeRatio=-0.191321795593124|RSquared=0.252383247052426|BetaMonths=6|Beta=0.531335296723166
@@ -265,7 +254,24 @@ Symbol=PSMT|AnalysisDate=2/4/2026 12:00:00 AM|EPSSlope=0.0399999618530273|Profit
Symbol=DCI|AnalysisDate=2/4/2026 12:00:00 AM|EPSSlope=0.0800000429153442|ProfitMarginSlope=0.454849243164063|PriceSlope=0.00170455784568539|Volatility=1.97363579273224|Volume=0|Violation=False|Slope=0.00170455784568539|Score=1.31671434176791|AnnualizedReturn=1.53656372692812|SharpeRatio=-0.0863957755266661|RSquared=0.856921401106001|BetaMonths=6|Beta=0.807390077629777
Symbol=VIV|AnalysisDate=2/5/2026 12:00:00 AM|EPSSlope=0.0150000154972076|ProfitMarginSlope=0.774349212646484|PriceSlope=0.0018972653337033|Volatility=0.896089375019073|Volume=0|Violation=False|Slope=0.0018972653337033|Score=1.34104616717728|AnnualizedReturn=1.61302429994768|SharpeRatio=0.0802914461607737|RSquared=0.831386214839278|BetaMonths=6|Beta=0.658849608391599
Symbol=ACA|AnalysisDate=2/10/2026 12:00:00 AM|EPSSlope=0.569999992847443|ProfitMarginSlope=2.10593795776367|PriceSlope=0.00135427268924872|Volatility=4.8927788734436|Volume=0|Violation=False|Slope=0.00135427268924872|Score=0.959654963471782|AnnualizedReturn=1.40674245753235|SharpeRatio=-0.232548548203499|RSquared=0.682182412518612|BetaMonths=6|Beta=0.558306829669136
TotalStopLimits=211
Symbol=SPHR|AnalysisDate=2/17/2026 12:00:00 AM|EPSSlope=1.34500002861023|ProfitMarginSlope=2.51224708557129|PriceSlope=0.00523907012952609|Volatility=8.95045948028564|Volume=0|Violation=False|Slope=0.00523907012952609|Score=3.20870287565322|AnnualizedReturn=3.74434114645189|SharpeRatio=0.218491066644505|RSquared=0.856947257248118|BetaMonths=6|Beta=3.21248816680421
Symbol=AEIS|AnalysisDate=2/17/2026 12:00:00 AM|EPSSlope=0.00999999046325684|ProfitMarginSlope=0.814477920532227|PriceSlope=0.00426370997524409|Volatility=22.9871597290039|Volume=0|Violation=False|Slope=0.00426370997524409|Score=2.66714040431645|AnnualizedReturn=2.92839623692391|SharpeRatio=0.421589812378226|RSquared=0.910785354347438|BetaMonths=6|Beta=3.7968120892911
Symbol=VRT|AnalysisDate=2/17/2026 12:00:00 AM|EPSSlope=0.379999995231628|ProfitMarginSlope=2.02690315246582|PriceSlope=0.00388394295179174|Volatility=23.7274208068848|Volume=0|Violation=False|Slope=0.00388394295179174|Score=2.25005270680435|AnnualizedReturn=2.66113739591567|SharpeRatio=0.122049768039598|RSquared=0.845522937018489|BetaMonths=6|Beta=4.55406437863137
Symbol=PAHC|AnalysisDate=2/17/2026 12:00:00 AM|EPSSlope=0.300000011920929|ProfitMarginSlope=3.21435642242432|PriceSlope=0.00374364997253655|Volatility=5.95380306243896|Volume=0|Violation=False|Slope=0.00374364997253655|Score=2.12819854519029|AnnualizedReturn=2.56869963711064|SharpeRatio=0.184901261217851|RSquared=0.828512027815039|BetaMonths=6|Beta=6.70791192761971
Symbol=BWA|AnalysisDate=2/17/2026 12:00:00 AM|EPSSlope=0.295000016689301|ProfitMarginSlope=1.43632125854492|PriceSlope=0.00264878087635921|Volatility=6.97055912017822|Volume=0|Violation=False|Slope=0.00264878087635921|Score=1.73266971161757|AnnualizedReturn=1.94934375656964|SharpeRatio=0.0294760197755666|RSquared=0.888847698502722|BetaMonths=6|Beta=0.749787397929552
Symbol=HWM|AnalysisDate=2/17/2026 12:00:00 AM|EPSSlope=0.0750000476837158|ProfitMarginSlope=0.679458618164063|PriceSlope=0.00226567934237701|Volatility=15.8062677383423|Volume=0|Violation=False|Slope=0.00226567934237701|Score=1.49471479981506|AnnualizedReturn=1.76994981712852|SharpeRatio=0.104874604929631|RSquared=0.844495581371914|BetaMonths=6|Beta=1.4716901369434
Symbol=TIMB|AnalysisDate=2/17/2026 12:00:00 AM|EPSSlope=0.0699999928474426|ProfitMarginSlope=1.20602607727051|PriceSlope=0.00199482432587108|Volatility=1.30688989162445|Volume=0|Violation=False|Slope=0.00199482432587108|Score=1.36503022074127|AnnualizedReturn=1.65317177427138|SharpeRatio=0.147781257421024|RSquared=0.825703802826477|BetaMonths=6|Beta=3.19165382029475
Symbol=MSGE|AnalysisDate=2/18/2026 12:00:00 AM|EPSSlope=0.190000027418137|ProfitMarginSlope=9.9661922454834|PriceSlope=0.00257838089510811|Volatility=1.4681134223938|Volume=0|Violation=False|Slope=0.00257838089510811|Score=1.72098906390133|AnnualizedReturn=1.91506580615047|SharpeRatio=0.138978808993797|RSquared=0.898657925160674|BetaMonths=6|Beta=1.34875445513012
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
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
Symbol=MPC|AnalysisDate=3/4/2026 12:00:00 AM|EPSSlope=1.98000001907349|ProfitMarginSlope=1.7620415687561|PriceSlope=0.00139964028977894|Volatility=7.6709098815918|Volume=0|Violation=False|Slope=0.00139964028977894|Score=0.906034636922709|AnnualizedReturn=1.42291751665396|SharpeRatio=-0.0262963567920841|RSquared=0.636744313228555|BetaMonths=6|Beta=3.22659752171988
Symbol=GPRE|AnalysisDate=3/5/2026 12:00:00 AM|EPSSlope=0.51500004529953|ProfitMarginSlope=0.921499490737915|PriceSlope=0.00507912376904452|Volatility=0.632249653339386|Volume=0|Violation=False|Slope=0.00507912376904452|Score=2.91691129240431|AnnualizedReturn=3.59642101983529|SharpeRatio=0.294963134062473|RSquared=0.811059460590603|BetaMonths=6|Beta=0.39196687303021
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=
@@ -477,3 +483,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=

View File

@@ -1,23 +1,23 @@
SESSIONv1.00
LastUpdated=2/2/2026 11:09:46 AM
TradeDate=2/27/2026
LastUpdated=3/2/2026 10:14:42 AM
TradeDate=3/31/2026
StartDate=1/1/2018
AnalysisDate=2/2/2026
Cycle=97
CashBalance=284.05
AnalysisDate=3/2/2026
Cycle=98
CashBalance=1151.44
NonTradeableCash=8221.84
Verbose=True|BenchmarkMode=False|BenchmarkModeSymbol=SPY|HoldingPeriod=3|MaxPositions=3|NoTradeSymbols=ANTM,ETE,TGE,VCISY,BIREF,CSRA,LSXMK,KKPNY,CNNE,EDR,GBTC,YOKU,PNY,RFMD,ASAZY,PSDO|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|UseCalcBeta=True|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=SCOREINDICATOR|IncludeTradeMasterForSymbolsHeld=True
TotalActivePositions=9
Slot=0|Symbol=DCO|PurchaseDate=1/30/2026 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=21|PurchasePrice=111.96|CurrentPrice=110.95|Volume=106472|Return1D=0|ZacksRank=3-Hold|CumReturn252=0|IDIndicator=-9.56175298804781|Score=1.24553280104877|MaxDrawdown=-0.468718945980072|MaxUpside=0.306542038917542|Velocity=0.924237560192616|PE=36.44|Beta=0.97|SharpeRatio=0
Slot=0|Symbol=ENSG|PurchaseDate=1/30/2026 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=13|PurchasePrice=171.54|CurrentPrice=171.66|Volume=396022|Return1D=0|ZacksRank=3-Hold|CumReturn252=0|IDIndicator=-7.17131474103586|Score=1.02314117847296|MaxDrawdown=-0.368556797504425|MaxUpside=0.241224050521851|Velocity=0.716187911430281|PE=31.6|Beta=0.73|SharpeRatio=0
Slot=0|Symbol=MDT|PurchaseDate=1/30/2026 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=22|PurchasePrice=102.85|CurrentPrice=102.96|Volume=6046285|Return1D=0|ZacksRank=4-Sell|CumReturn252=0|IDIndicator=-10.3585657370518|Score=0.518428403011861|MaxDrawdown=-0.244328439235687|MaxUpside=0.171293973922729|Velocity=0.865293185419968|PE=26.67|Beta=0.74|SharpeRatio=0
Slot=1|Symbol=TRVI|PurchaseDate=11/28/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=188|PurchasePrice=13.07|CurrentPrice=13.19|Volume=1325673|Return1D=0|ZacksRank=3-Hold|CumReturn252=0|IDIndicator=2.39043824701196|Score=2.75368855301141|MaxDrawdown=-0.744615375995636|MaxUpside=0.345381498336792|Velocity=1|PE=0|Beta=-0.43|SharpeRatio=0
Slot=1|Symbol=DRD|PurchaseDate=11/28/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=82|PurchasePrice=30.6|CurrentPrice=30.18|Volume=295934|Return1D=0|ZacksRank=3-Hold|CumReturn252=0|IDIndicator=-8.76494023904382|Score=1.50229817076201|MaxDrawdown=-0.697993636131287|MaxUpside=0.451048970222473|Velocity=0.934228187919463|PE=19.65|Beta=0.49|SharpeRatio=0
Slot=1|Symbol=NFG|PurchaseDate=11/28/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=30|PurchasePrice=82.45|CurrentPrice=82.45|Volume=236813|Return1D=0|ZacksRank=3-Hold|CumReturn252=0|IDIndicator=-9.56175298804781|Score=1.47874375251707|MaxDrawdown=-0.37163895368576|MaxUpside=0.0953423976898193|Velocity=0.658599827139153|PE=31.63|Beta=0.3|SharpeRatio=0
Slot=1|Symbol=GTX|PurchaseDate=2/27/2026 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=123|PurchasePrice=19.63|CurrentPrice=20.36|Volume=2065018|Return1D=0|ZacksRank=3-Hold|CumReturn252=0|IDIndicator=-1.59362549800797|Score=1.78686800402217|MaxDrawdown=-0.585076332092285|MaxUpside=0.361035346984863|Velocity=0.931308749096167|PE=13.51|Beta=0.63|SharpeRatio=0
Slot=1|Symbol=ALLE|PurchaseDate=2/27/2026 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=15|PurchasePrice=160.87|CurrentPrice=161.15|Volume=483322|Return1D=0|ZacksRank=3-Hold|CumReturn252=0|IDIndicator=-7.56972111553785|Score=1.09215924523576|MaxDrawdown=-0.339611113071442|MaxUpside=0.143267035484314|Velocity=0.659949622166247|PE=24.3|Beta=0.91|SharpeRatio=0
Slot=1|Symbol=TARS|PurchaseDate=2/27/2026 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=33|PurchasePrice=73.61|CurrentPrice=75.52|Volume=410748|Return1D=0|ZacksRank=4-Sell|CumReturn252=0|IDIndicator=-4.7808764940239|Score=0.769641209159601|MaxDrawdown=-0.529511570930481|MaxUpside=0.466769695281982|Velocity=0.832618025751073|PE=0|Beta=0.71|SharpeRatio=0
Slot=2|Symbol=RKLB|PurchaseDate=12/31/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=35|PurchasePrice=70.63|CurrentPrice=69.76|Volume=17495818|Return1D=0|ZacksRank=3-Hold|CumReturn252=0|IDIndicator=-9.56175298804781|Score=2.34285560119306|MaxDrawdown=-0.845531940460205|MaxUpside=0.92852771282196|Velocity=0.900553359683795|PE=0|Beta=2.96|SharpeRatio=0
Slot=2|Symbol=TPB|PurchaseDate=12/31/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=22|PurchasePrice=109|CurrentPrice=108.4|Volume=132784|Return1D=0|ZacksRank=1-Strong Buy|CumReturn252=0|IDIndicator=-15.9362549800797|Score=1.44361960940429|MaxDrawdown=-0.547061681747437|MaxUpside=0.289932131767273|Velocity=0.947395388556789|PE=37.1|Beta=1.04|SharpeRatio=0
Slot=2|Symbol=DHC|PurchaseDate=12/31/2025 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=508|PurchasePrice=4.86|CurrentPrice=4.85|Volume=583294|Return1D=0|ZacksRank=3-Hold|CumReturn252=0|IDIndicator=-1.99203187250997|Score=1.32742152530989|MaxDrawdown=-0.556521773338318|MaxUpside=0.754902005195618|Velocity=0.867313915857606|PE=0.37|Beta=2.29|SharpeRatio=0
TotalPositions=255
TotalPositions=258
Symbol=LOPE|PurchaseDate=1/31/2018 12:00:00 AM|SellDate=4/30/2018 12:00:00 AM|Shares=11|PurchasePrice=92.52|CurrentPrice=103.99|Volume=177499|Return1D=0|ZacksRank=|CumReturn252=0|IDIndicator=-13.1474103585657|Score=NaN|MaxDrawdown=0|MaxUpside=0|Velocity=0.919762641898865|PE=24.89|Beta=1.38|SharpeRatio=NaN
Symbol=MRCY|PurchaseDate=1/31/2018 12:00:00 AM|SellDate=4/30/2018 12:00:00 AM|Shares=23|PurchasePrice=47.69|CurrentPrice=32.08|Volume=481172|Return1D=0|ZacksRank=|CumReturn252=0|IDIndicator=-6.77290836653387|Score=NaN|MaxDrawdown=0|MaxUpside=0|Velocity=0.74313408723748|PE=56.5|Beta=0.44|SharpeRatio=NaN
Symbol=SHV|PurchaseDate=2/28/2018 12:00:00 AM|SellDate=5/31/2018 12:00:00 AM|Shares=35|PurchasePrice=110.21|CurrentPrice=110.26|Volume=0|Return1D=0|ZacksRank=|CumReturn252=0|IDIndicator=0|Score=NaN|MaxDrawdown=0|MaxUpside=0|Velocity=0|PE=0|Beta=0|SharpeRatio=NaN
@@ -273,3 +273,6 @@ Symbol=KGC|PurchaseDate=9/30/2025 12:00:00 AM|SellDate=12/31/2025 12:00:00 AM|Sh
Symbol=ATAI|PurchaseDate=10/31/2025 12:00:00 AM|SellDate=1/30/2026 12:00:00 AM|Shares=421|PurchasePrice=5.85|CurrentPrice=3.74|Volume=4451435|Return1D=0|ZacksRank=3-Hold|CumReturn252=0|IDIndicator=-4.38247011952191|Score=2.00926038960802|MaxDrawdown=-0.780538320541382|MaxUpside=1.1074378490448|Velocity=0.837742504409171|PE=0.3|Beta=2.08|SharpeRatio=0
Symbol=GH|PurchaseDate=10/31/2025 12:00:00 AM|SellDate=1/30/2026 12:00:00 AM|Shares=26|PurchasePrice=94.07|CurrentPrice=113.68|Volume=3919282|Return1D=0|ZacksRank=3-Hold|CumReturn252=0|IDIndicator=-3.18725099601593|Score=1.87048017006212|MaxDrawdown=-0.700385630130768|MaxUpside=0.393415093421936|Velocity=1|PE=0|Beta=2.03|SharpeRatio=0
Symbol=WOR|PurchaseDate=10/31/2025 12:00:00 AM|SellDate=1/30/2026 12:00:00 AM|Shares=43|PurchasePrice=55.65|CurrentPrice=55.87|Volume=121804|Return1D=0|ZacksRank=4-Sell|CumReturn252=0|IDIndicator=-1.99203187250995|Score=1.28685684129769|MaxDrawdown=-0.433150291442871|MaxUpside=0.218586325645447|Velocity=0.534509927513394|PE=26.76|Beta=1.03|SharpeRatio=0
Symbol=TRVI|PurchaseDate=11/28/2025 12:00:00 AM|SellDate=2/27/2026 12:00:00 AM|Shares=188|PurchasePrice=13.07|CurrentPrice=11.75|Volume=1325673|Return1D=0|ZacksRank=3-Hold|CumReturn252=0|IDIndicator=2.39043824701196|Score=2.75368855301141|MaxDrawdown=-0.744615375995636|MaxUpside=0.345381498336792|Velocity=1|PE=0|Beta=-0.43|SharpeRatio=0
Symbol=DRD|PurchaseDate=11/28/2025 12:00:00 AM|SellDate=2/27/2026 12:00:00 AM|Shares=82|PurchasePrice=30.6|CurrentPrice=38.48|Volume=295934|Return1D=0|ZacksRank=3-Hold|CumReturn252=0|IDIndicator=-8.76494023904382|Score=1.50229817076201|MaxDrawdown=-0.697993636131287|MaxUpside=0.451048970222473|Velocity=0.934228187919463|PE=19.65|Beta=0.49|SharpeRatio=0
Symbol=NFG|PurchaseDate=11/28/2025 12:00:00 AM|SellDate=2/27/2026 12:00:00 AM|Shares=30|PurchasePrice=82.45|CurrentPrice=91.99|Volume=236813|Return1D=0|ZacksRank=3-Hold|CumReturn252=0|IDIndicator=-9.56175298804781|Score=1.47874375251707|MaxDrawdown=-0.37163895368576|MaxUpside=0.0953423976898193|Velocity=0.658599827139153|PE=31.63|Beta=0.3|SharpeRatio=0

View File

@@ -1,22 +1,21 @@
MGSHSESSIONv2.00
LastUpdated=2/13/2026 8:56:00 PM
TradeDate=2/17/2026
LastUpdated=3/10/2026 9:29:44 PM
TradeDate=3/11/2026
StartDate=3/31/2025
AnalysisDate=2/13/2026
Cycle=11
CashBalance=2932.39
AnalysisDate=3/10/2026
Cycle=12
CashBalance=3886.05
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=99.93|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=77.9387144756317|LastStopAdjustment=1/15/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=220.79|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=131.77|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=81.59|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=64.936|LastStopAdjustment=1/1/0001 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=99.49|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.19|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.34|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
TotalActivePositions=6
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=217.04|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=210.485930538177|LastStopAdjustment=3/2/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=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=81.88|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=89.89|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=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=91.17|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=85.4851430559158|LastStopAdjustment=3/2/2026 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=16.49|Comment=
Slot=2|Symbol=NWN|PurchaseDate=2/27/2026 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=18|PurchasePrice=53|CurrentPrice=51.62|Volume=468654|Return1D=0|CumReturn252=0|IDIndicator=-17.1314741035857|Score=0.661596031246807|Velocity=1|PE=20.04|Beta=-0.0208556368428403|InitialStopLimit=42.4|TrailingStopLimit=42.4|LastStopAdjustment=1/1/0001 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=10.608|Comment=Price changed on 3/2/2026 from $53.04 to $53.00
Slot=2|Symbol=ALLE|PurchaseDate=2/27/2026 12:00:00 AM|SellDate=1/1/0001 12:00:00 AM|Shares=6|PurchasePrice=160.87|CurrentPrice=147.53|Volume=483322|Return1D=0|CumReturn252=0|IDIndicator=-7.56972111553785|Score=1.09215924523576|Velocity=0.659949622166247|PE=24.3|Beta=0.326218892554503|InitialStopLimit=128.7|TrailingStopLimit=128.7|LastStopAdjustment=1/1/0001 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=32.23|Comment=Price changed on 3/2/2026 from $161.15 to $160.87
TotalPositions=20
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.
Symbol=RGLD|PurchaseDate=4/30/2025 12:00:00 AM|SellDate=7/7/2025 12:00:00 AM|Shares=6|PurchasePrice=179.06|CurrentPrice=162|Volume=872755|Return1D=0|CumReturn252=0|IDIndicator=-12.3505976095618|Score=0.879340632979787|Velocity=0.8398891966759|PE=36.94|Beta=0.672718546494456|InitialStopLimit=143.25|TrailingStopLimit=162.603214492798|LastStopAdjustment=6/2/2025 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=36.542|Comment=Manual close.
@@ -34,7 +33,10 @@ Symbol=SH|PurchaseDate=9/15/2025 12:00:00 AM|SellDate=11/20/2025 12:00:00 AM|Sha
Symbol=PSO|PurchaseDate=4/30/2025 12:00:00 AM|SellDate=1/16/2026 12:00:00 AM|Shares=69|PurchasePrice=15.98|CurrentPrice=12.62|Volume=894303|Return1D=0|CumReturn252=0|IDIndicator=-11.9521912350598|Score=1.15269564166514|Velocity=0.737122557726465|PE=19.2|Beta=0.0342052512015139|InitialStopLimit=12.78|TrailingStopLimit=12.78|LastStopAdjustment=1/1/0001 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=3.25|Comment=Closed due to trailing stop.
Symbol=NYT|PurchaseDate=8/29/2025 12:00:00 AM|SellDate=2/4/2026 12:00:00 AM|Shares=14|PurchasePrice=59.38|CurrentPrice=61.34|Volume=1582847|Return1D=0|CumReturn252=0|IDIndicator=-10.3585657370518|Score=-0.0546472654561472|Velocity=0.878823529411765|PE=30.96|Beta=0.759120105784337|InitialStopLimit=47.5|TrailingStopLimit=66.0039285755157|LastStopAdjustment=1/7/2026 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=11.968|Comment=Manual close.
Symbol=NEU|PurchaseDate=11/28/2025 12:00:00 AM|SellDate=2/12/2026 12:00:00 AM|Shares=1|PurchasePrice=761.43|CurrentPrice=607|Volume=47592|Return1D=0|CumReturn252=0|IDIndicator=-10.7569721115538|Score=1.32888913206578|Velocity=0.735010364152827|PE=16.57|Beta=0.634028454808648|InitialStopLimit=609.14|TrailingStopLimit=609.14|LastStopAdjustment=1/1/0001 12:00:00 AM|PositionRiskPercentDecimal=0.2|R=152.706|Comment=Closed due to trailing stop.
TotalStopLimits=36
Symbol=TPB|PurchaseDate=12/31/2025 12:00:00 AM|SellDate=3/2/2026 12:00:00 AM|Shares=6|PurchasePrice=109|CurrentPrice=106.84|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=Manual close.
Symbol=USFD|PurchaseDate=9/30/2025 12:00:00 AM|SellDate=3/6/2026 12:00:00 AM|Shares=12|PurchasePrice=76.21|CurrentPrice=90.2|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=Manual close.
Symbol=ALHC|PurchaseDate=1/30/2026 12:00:00 AM|SellDate=3/9/2026 12:00:00 AM|Shares=64|PurchasePrice=22.45|CurrentPrice=17.96|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=Manual close.
TotalStopLimits=40
Symbol=EXC|AnalysisDate=4/30/2025 12:00:00 AM|PreviousStop=36.61|NewStop=42.7107857322693|CurrentPriceLow=42.6|CurrentPriceClose=46.9|PriceTrendIndicatorSlope=0.0870828032493591|StopLimitId=EXC20250331120000AM
Symbol=SXT|AnalysisDate=4/30/2025 12:00:00 AM|PreviousStop=59.18|NewStop=85.4177850723267|CurrentPriceLow=92.4|CurrentPriceClose=93.95|PriceTrendIndicatorSlope=0.931879639625549|StopLimitId=SXT20250331120000AM
Symbol=MO|AnalysisDate=5/7/2025 12:00:00 AM|PreviousStop=47.93|NewStop=56.1565003347397|CurrentPriceLow=56.16|CurrentPriceClose=60.91|PriceTrendIndicatorSlope=0.217300787568092|StopLimitId=MO20250331120000AM
@@ -71,5 +73,9 @@ Symbol=USFD|AnalysisDate=1/15/2026 12:00:00 AM|PreviousStop=73.5799289083481|New
Symbol=CAH|AnalysisDate=1/30/2026 12:00:00 AM|PreviousStop=194.335500230789|NewStop=198.937214622498|CurrentPriceLow=210.01|CurrentPriceClose=212.42|PriceTrendIndicatorSlope=0.434736758470535|StopLimitId=CAH20250930120000AM
Symbol=TPB|AnalysisDate=1/30/2026 12:00:00 AM|PreviousStop=87.2|NewStop=106.557785625458|CurrentPriceLow=117.87|CurrentPriceClose=121.15|PriceTrendIndicatorSlope=0.970541477203369|StopLimitId=TPB20251231120000AM
Symbol=NFG|AnalysisDate=1/30/2026 12:00:00 AM|PreviousStop=65.96|NewStop=77.9947861814499|CurrentPriceLow=82.54|CurrentPriceClose=83.75|PriceTrendIndicatorSlope=0.215090155601501|StopLimitId=NFG20251128120000AM
Symbol=USFD|AnalysisDate=2/17/2026 12:00:00 AM|PreviousStop=77.9387144756317|NewStop=90.4222861194611|CurrentPriceLow=96.59|CurrentPriceClose=97.11|PriceTrendIndicatorSlope=0.7435262799263|StopLimitId=USFD20250930120000AM
Symbol=XEL|AnalysisDate=2/23/2026 12:00:00 AM|PreviousStop=64.936|NewStop=77.5441425132751|CurrentPriceLow=81.78|CurrentPriceClose=83.35|PriceTrendIndicatorSlope=0.34472182393074|StopLimitId=XEL20251031120000AM
Symbol=CAH|AnalysisDate=3/2/2026 12:00:00 AM|PreviousStop=198.937214622498|NewStop=210.485930538177|CurrentPriceLow=228.1|CurrentPriceClose=229.88|PriceTrendIndicatorSlope=0.661804258823395|StopLimitId=CAH20250930120000AM
Symbol=NFG|AnalysisDate=3/2/2026 12:00:00 AM|PreviousStop=77.9947861814499|NewStop=85.4851430559158|CurrentPriceLow=90.74|CurrentPriceClose=92.66|PriceTrendIndicatorSlope=0.335458546876907|StopLimitId=NFG20251128120000AM
TotalHedgePositions=0
TotalPricingExceptions=0

Binary file not shown.

Binary file not shown.

Binary file not shown.