01 The Platform
For years I've been fascinated by the idea that markets move in fractal waves and repeating cycles rather than random noise. SharpTechnicals is my long-running attempt to turn that intuition into something measurable. It ingests market price data, enriches every bar with a dense set of analytical features β fractal events, support/resistance levels, technical indicators, cycle data β and exposes the results through a web UI and a set of offline machine-learning experiments.
Unlike a quick POC, this one grew into a proper multi-project solution β 28 projects with a real layered architecture, a production data store, background sync jobs, and pluggable data feeds. It was recently modernized from .NET Framework 4.8 to .NET 8 LTS without rewriting the core.
The stack:
- Core β C# / .NET 8 (migrated from Framework 4.8)
- Data store β MongoDB, behind a generic repository
- Web UI β ASP.NET Core MVC with HighCharts OHLC rendering
- Background sync β a .NET Generic Host Windows Service
- Feeds β Tiingo, Yahoo Finance, Quandl, Financial Modeling Prep, CSI Data
- ML β GeneticSharp (strategy evolution), Accord.NET (decision trees / random forest)
- Desktop charting β WPF + SciChart
02 A Layered Architecture
The solution is strictly layered. Everything depends on a shared Entities project; interfaces are defined once and implemented by swappable storage and connector projects. That's what keeps a feed (Tiingo vs. Yahoo) or a store (MongoDB vs. CSV) interchangeable β and it's what let me migrate the whole solution to .NET 8 without touching the analytics engine.
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SharpTechnicals.Entities β
β domain models Β· enums Β· interfaces β
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β² (everything depends on this)
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Pattern (interfaces) Β· Utilities β
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β²
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Features (analytics) βββΊ Markets β
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β² β²
βββββββββββ΄βββββββββββ ββββββββββββββ΄βββββββββββ
β Storage β β Connectors β
β : IPriceDataStorageβ β : IRawDataConnector β
β MongoDB / CSV β β Tiingo Β· Yahoo Β· β
β β β Quandl Β· FMP Β· CSI β
ββββββββββββββββββββββ βββββββββββββββββββββββββ
β²
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Web (MVC) Β· Jobs (sync) Β· FileGenerator (ETL) β
β Genetic Β· RandomForest Β· Charting (WPF) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
03 The Core Idea: Fractal Events
Everything in the platform is built on one foundational concept β fractal classification. Each price bar is examined with a lookback/lookahead window and classified as a High, a Low, or Neutral, at four nested scales.
Reaction βββΊ Minor βββΊ Intermediate βββΊ Primary (smallest) (largest)
A Primary high is a high that dominates a large window; a Reaction high is a small local wiggle. Once every bar carries this four-tier classification, the rest of the analysis falls out of it: support/resistance levels come from where past fractal events clustered, cycles are scored by how often their resonance points land on fractal events, and trade signals fire on level breaches. Almost every later feature reduces to one question β "where did the fractal events land?" β which kept the codebase conceptually coherent as it grew.
04 PriceDataFeature: The Central Object
The heart of the pipeline is the PriceDataFeature β a raw OHLCV bar enriched with roughly 60 computed properties. A PriceDataFeatureGenerator runs three finders in sequence to build it.
Raw OHLCV bars
β
βΌ
ββββββββββββββββ ββββββββββββββββ βββββββββββββββββββββββββββ
β EventFinder βββΊβ LevelFinder βββΊβ TechnicalIndicatorFinderβ
β fractal β β support / β β RSI, stochastics, MAs, β
β High/Low/ β β resistance β β volatility, candlestick β
β Neutral Γ4 β β levels β β patterns, ~60 flags β
ββββββββββββββββ ββββββββββββββββ βββββββββββββββββββββββββββ
β
βΌ
PriceDataFeature (stored in MongoDB, pre-computed offline)
The indicators include RSI with threshold flags, stochastic oscillators, moving-average crossovers, intraday/overnight volatility buckets, candlestick patterns (Doji, DragonFly, Gravestone), and higher-high/lower-low comparisons at Fibonacci-spaced lookbacks (1, 2, 3, 5, 8, 13, 21, 120 bars). Because computing these is expensive, features are generated offline by an ETL console tool and stored in MongoDB β the web layer just reads pre-computed features rather than recomputing them per request.
05 The Market Abstraction
Working with raw storage directly would be painful, so the Markets layer wraps it in a clean, lazy-loaded object model. A single BaseMarket exposes one collection per time level, and each collection only loads when first accessed.
BaseMarket
ββ Daily (lazy-loaded PriceDataCollection)
ββ Weekly "
ββ Monthly "
ββ Quarterly "
ββ Yearly "
UpdateableMarket adds live sync: it asks the connector for any bars newer than the last stored one, inserts them, and rolls the new daily data up into the weekly/monthly/quarterly/yearly views. TimeLevel is part of the compound key for every storage query β it's passed explicitly everywhere, never inferred.
06 Resolving Data Feeds
The MarketUniverse is the single factory for all of this. It resolves the correct data feed by matching a market's UpdateConnectorName against registered connector names β so adding a new data source is just registering one more connector, with no change to the analytics engine.
MarketData.UpdateConnectorName = "tiingo"
β
βΌ
MarketUniverse ββlookupβββΊ { "tiingo": TiingoConnector,
"yahoo" : YahooConnector,
"fmp" : FmpConnector, ... }
β
βΌ
fully-wired UpdateableMarket
07 Keeping Data Fresh
A .NET Generic Host Windows Service (MarketSyncJob) runs on a 24-hour loop. It pulls every market from MongoDB, calls UpdateAll() on each, and inserts any new price bars from the live feed β keeping the local store current without manual intervention.
every 24h βββΊ MarketUniverse.GetUpdateableMarkets()
β for each market
βΌ
UpdateAll() βββΊ fetch new bars
βββΊ insert
βββΊ roll up time levels
08 Web Layer, ML & Reflections
An ASP.NET Core MVC app sits on top for browsing and annotation. It never touches storage directly β controllers depend on service interfaces that orchestrate the MarketUniverse and feature store behind the scenes:
- Markets β paginated instrument list, a HighCharts OHLC viewer, and manual price-bar categorization that feeds labeled training data back into the system
- Cycles β define a cycle (first wave date + length) and project its wave positions forward onto the chart's date axis as an overlay
- Features β manage the pre-computed support/resistance chart annotations
The same feature pipeline feeds two offline ML experiments that treat the analytics output as training data. Genetic (GeneticSharp) evolves buy/sell rule sets against SPY daily data β it's also how the platform's default support/resistance parameters were originally tuned. RandomForest (Accord.NET) trains a C4.5 decision tree to classify trade direction from PriceDataFeature inputs, using a "perfect trade" oracle (buy every reaction low, sell every reaction high) to generate ideal labels. There's also an experimental computer-vision angle β a Gramian Angular Field pipeline that encodes price windows as image matrices.
The thing I'm most happy with is the layering. Because storage and feeds are pure interfaces, I've swapped MongoDB for CSV in the experiments, added providers like FMP and CSI Data without touching the analytics engine, and migrated the whole solution from .NET Framework 4.8 to .NET 8 β all without rewriting the core.