Skip to content

Repository files navigation

EventGraph

EventGraph is a small .NET sample that demonstrates an event-driven market quote pipeline using simulated instruments and basket aggregation.

Overview

  • Simulates stock-like assets with EquitySource.
  • Subscribes the same QuoteSubscriber to both individual quotes and an aggregated basket.
  • Emits basket updates only after all constituent values are available for the current cycle.
  • Validates the graph-node dependency graph at startup and rejects cycles before processing begins.
  • Keeps the sample self-contained, deterministic enough for tests, and suitable for public release as a demo project.

Node dependencies

flowchart LR
	equityDefinition["EquityDefinition"]
	rateDefinition["CurrencyRateDefinition"]
	basketDefinition["BasketDefinition"]
	optionDefinition["EquityOptionDefinition"]

	equity["EquitySource [ISpotSourceNode, IVolSourceNode]"]
	rate["CurrencyRateSource [IRateSourceNode]"]

	equityDefinition -.->|compiled into| equity
	rateDefinition -.->|compiled into| rate

	equity --> spot["SpotNode [ISpotNode]"]
	equity --> volatility["VolatilityNode [IVolNode]"]
	rate --> discount["RateCurveNode [IDiscountCurveNode]"]

	spot --> forward["ForwardCurveNode [IForwardCurveNode]"]
	discount --> forward
	forward --> option["EquityOptionNode [IEquityOptionNode]"]
	optionDefinition -.->|compiled into| option
	volatility --> option
	discount --> option

	spot -->|one or more constituents| basket["BasketSpotNode [ISpotNode]"]
	basketDefinition -.->|compiled into| basket
	basket --> basketForward["ForwardCurveNode [IForwardCurveNode]"]
	discount --> basketForward
	basketForward --> basketOption["EquityOptionNode [IEquityOptionNode]"]
	basket --> basketVolatility["BasketVolatilityNode [IVolNode, 30%]"]
	basketVolatility --> basketOption
	discount --> basketOption
Loading

Graph definitions

An EquityDefinition JSON definition in EventGraph/graph-definition uses the following fields:

{
	"type": "EquityDefinition",
	"name": "TSLA",
	"currency": "USD",
	"spot": 800.0,
	"volatility": 0.2,
	"meanTickTimeSeconds": 3.0
}

Every JSON file in graph-definition/ is a static *Definition specification compiled into live nodes and sources by GraphDefinitionCompiler. Each EquityDefinition compiles into an EquitySource. A BasketDefinition uses name, currency, constituents, and weights; before graph construction, the loader compiles it into a BasketSpotNode and materializes a SpotNode for each constituent. The application loads all JSON definitions from this folder at startup, in filename order. Terminal colors are assigned by QuoteSubscriber, not stored as node properties.

A CurrencyRateDefinition provides a named flat interestRate (for example, 0.02 for 2%) and compiles into a CurrencyRateSource. The loader materializes a dependent RateCurveNode when a forward curve or equity option needs a discount curve. Its DiscountFactor property is a date -> double function implemented as exp(-interestRate * (date - today) / 365).

An EquityOptionDefinition uses underlyer, maturity, strike, and optionType. Before graph construction, the loader compiles it into an EquityOptionNode and materializes its forward, volatility, and discount-curve dependencies. An equity underlyer uses VolatilityNode; a basket underlyer uses BasketVolatilityNode, currently fixed at 30%. The current example uses maturity: "1Y", meaning today plus one year, and sets the strike to the equity's current spot. Pricing uses the forward Black-Scholes form: discountFactor * (forward * N(d1) - strike * N(d2)), with the rate-curve discount factor at maturity.

Graph loading uses Kahn's algorithm to resolve dependencies. The loader builds an in-degree count for each node, processes dependency-free nodes first, and then releases dependent nodes as their prerequisites are created. This keeps startup ordering deterministic while avoiding repeated full scans of unresolved definitions.

A GraphSession coordinates the lifecycle of the active QuoteGraph against an external MarketClock. When the valuation date advances, the session recompiles a new graph, filters out expired contracts, adjusts relative tenors, reconnects subscriptions, and carries over running spot state.

The loaded graph also exposes stable node indices through QuoteGraph, which gives future runtime optimizations a dense representation without replacing the current node model. More specialized graph layouts such as compressed sparse row storage or parallel execution should be considered only after profiling shows that traversal or update propagation is a bottleneck.

How it works

Quote simulation

Each simulated spot follows a GBM-style lognormal process. The model does this at each step:

  1. Samples a random time interval from a Poisson distribution.
  2. Converts the interval into a volatility scaling over a year.
  3. Applies a normal shock and the Itô drift correction.
  4. Updates the current value and raises the tick event.

The underlying update is:

$$ S_{t+\Delta t} = S_t \exp\left(\sigma Z \sqrt{\Delta t} - \frac{1}{2}\sigma^2 \Delta t\right) $$

This is the standard base-process approach for a spot simulation. It does not add a pricing-specific convexity adjustment, because that is a payoff-level consideration rather than a spot-generation concern.

Basket aggregation

The basket does not publish on every constituent tick. Instead, it stores the latest values and emits a new basket value only after all constituents have provided an observation for the current cycle.

Prerequisites

  • .NET 8 SDK
  • A terminal with access to the repository root

Run the sample

dotnet run --project EventGraph/EventGraph.csproj

Customise the run with CLI options:

dotnet run --project EventGraph/EventGraph.csproj -- --ticks 3 --quiet

Available options:

  • --ticks <n>: Number of ticks to emit; defaults to continuous mode until interrupted
  • --quiet: Suppresses subscription and quote output
  • --basket-color <color>: Console color used for basket updates
  • --help: Displays usage information

Run tests and coverage

dotnet test EventGraph.Tests/EventGraph.Tests.csproj --collect:"XPlat Code Coverage" --results-directory ./coverage

Quality gate:

./scripts/quality.sh

The repo enforces at least 97% line coverage for the production code path.

Public-repo readiness checklist

  • Source is organized around a single library and a focused test project.
  • Test naming follows production types (AppOptions, BasketSpotNode, QuoteSubscriber, EquitySource, QuoteTick).
  • Coverage, quality checks, and clean test execution are included in the repo workflow.
  • The project includes a permissive open-source license in the root of the repository.
  • No secrets, credentials, or local-only environment artifacts are required to build or run the sample.

Notes

  • The sample uses Math.NET for random sampling.
  • The default execution model is continuous mode unless --ticks is supplied.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages