Core Concepts¶
Nuant Quantitative System is a modular engine for simulating and testing DeFi protocols and strategies across historical and synthetic data. It orchestrates agents, transactions, and on-chain state to replicate real market behavior in a controlled environment. Built for researchers and developers, NQS enables rapid strategy iteration, protocol design, and performance evaluation.
Architecture Components¶
The core engine is composed of several interconnected modules:
Protocols: Maintain internal state and process transactions (Uniswap, Compound, etc.)
Transactions: Contain instructions to be executed by smart contracts
States: Represent point-in-time snapshots of protocol state
Events: Output results from processed transactions
Wallets: Track positions and balances for agents
Observers: Collect metrics and simulation data
Oracles: Provide off-chain data like CEX prices
Simulations¶
Simulations orchestrate the execution of multi-protocol simulations. They coordinate between protocol factories, handle configuration loading, and execute the simulation from start to finish.
Key features:
Multi-protocol coordination
Agent behavior simulation
Block progression and timing
Result aggregation and analysis
Protocol Factory¶
Protocol Factory handle individual protocol integrations (e.g., Uniswap V3, Compound V2).
Key features:
Protocol-specific configuration handling
State management and persistence
Transaction processing and validation
Event emission and logging
Agents¶
An agent is an entity that can interact with various other entities of the simulation. The behaviour of the agent is defined by the user. The agent starts the simulation with a certain amount of tokens in his wallet and can perform two kind of actions:
timed actions: Timed actions are actions that are performed at a certain time of the simulation. The user can define the time at which the action is performed and the action itself. See action for the description of the action.
continuous actions: Continuous actions are actions that are performed continuously during the simulation. The user can define the frequency of the action and the action itself. See action for the description of the action.
Agent Action¶
An agent action is defined by a protocol, an action to perform on that protocol and the arguments of that action. A condition can be attached to the action. In that case the action can be executed only if the condition is satisfied.
Data Flow¶
Understanding how data flows through the simulation:
Configuration Loading: Simulation parameters and protocol settings are loaded
Protocol Initialization: Each protocol factory sets up its initial state
Agent Creation: Market participants are instantiated with their strategies
Block Processing: For each simulation block:
Agents generate transactions based on their strategies
Miners collect and order transactions
Protocols process transactions and update their state
Events are emitted and collected by observers
Wallets are updated with new positions
Result Aggregation: Final metrics and analysis are compiled
Generator¶
Generators are introduced to manage the creation of the simulated or historical real-world objects needed by the simulation. These include:
initial entities state, such as Tokens or Protocols. protocol transactions. There are two main classes of generators, random and historical which are used in simulation and backtesting mode respectively.
Historical Generator¶
The historical generator loads data from the Nuant proprietary data service DataQuasar (DTQ). It is used during backtesting to load all the events emitted by the protocol between two given timestamps (or block numbers). The events data are used to create the historical transactions that are replayed during the backtest.
The generator also fetches protocols state at a given timestamp (or block) in the past, which includes all the protocols internal variables and balances. This provides the initial state of the protocol during backtesting, but can also be used as a starting point for a forward-looking simulation.
Random Generator¶
The random generator is used for forward-looking simulations, where it generates random transactions across all protocols. It is made up of two main components:
The random transaction generator.
The random number generator.
Observer¶
Observers are used to collect metrics from the different entities of the simulation. Any component of the architecture, needing to use the state or a metric produced by another member of the simulation, can do so by querying the corresponding observer.
All observers provide their metrics through a dictionary attribute and need to implement a function collect_observables which gathers all observables generated at a given block.
Protocol¶
This module defines the logic of the protocols supported by the Nuant Core Engine.
Although each protocol Nuant Core Engine is instantiated in a different class, all of them need to extend a common interface thus providing some basic, common functionalities, which are:
the possibility to be initialised from a state object.
a method returning the current state of the protocol.
a function to process transactions sent to the protocol.
a method to execute a list of transactions.
Spots¶
The spot oracle is responsible for orchestrating the generation of all processes and provide spot values to all entities of the simulation.
An instance of SpotOracle is constructed from:
a DeterministicSpotProcessArray representing the deterministic spot processes in the simulation
a StochasticSpotProcessArray, containing all stochastic processes to simulate and their correlation matrix
the numéraire used in the simulation
the start and end timestamp
an optional RandomGenerator, used to generate the random increments needed to evolve the stochastic processes
a seed, used in the random number generation
State¶
States are a collection of dataclasses representing the snapshot of an entity of the simulation at a given block. An instance of the particular state class can be used to initialise the corresponding entity.
Entities that currently implement a state class are
tokens
wallets
protocols
Transaction¶
As in the blockchain, in the Nuant Core Engine each Agent willing to interact with protocols has to submit transactions.
The Transaction class defines an interface that every protocol transaction needs to extend. These are the basic attributes that every transaction needs to provide:
block_number: the block number at which the transaction is executed
block_index: the block index at which the transaction is executed
block_timestamp: the block timestamp corresponding to block_number
sender_wallet: this is an optional attribute specifying the wallet submitting the transaction. The variable is optional as the Nuant Core Engine can accept (if possible) transactions without any information about the transaction sender, when this is not necessary to track any metrics or to perform any sanity check.