Configuration-based approach¶
The configuration-based approach allows you to define your simulation using YAML or JSON configuration files. This approach is:
Declarative: Define what you want to simulate rather than how to simulate it
Structured: Clear organization of simulation parameters, market conditions, and agent strategies
Reusable: Easily share, version, and modify configurations without changing code
Accessible: Ideal for users who prefer configuration over programming
This approach is recommended for:
Users who want to quickly set up and run simulations without writing much code
Scenarios where you need to run many variations of similar simulations
When you want to clearly separate simulation parameters from execution logic
For reproducible research where configuration can be shared and versioned
Let’s start with a comprehensive configuration file that demonstrates all the key features of the NQS SDK. This example shows how to set up a complex simulation with multiple protocols, agents, and market conditions.
For detailed configuration syntax, see Config file schema.
Configuration File Structure¶
Here’s a complete configuration file with inline comments explaining each section:
version: 1.0.0 # Configuration format version
# Global simulation parameters
common:
block_number_start: 18725000 # Starting Ethereum block number
block_number_end: 18726000 # Ending block (1000 blocks total)
block_step_metrics: 100 # Collect metrics every 100 blocks
arbitrage_block_frequency: 5 # Check arbitrage opportunities every 5 blocks
plot_output: False # Disable chart generation
save_metrics: False # Don't save metrics to file
numeraire: USDC # Base currency for calculations
gas_fee_ccy: ETH # Currency for gas fees
gas_fee: 0.0000000001 # Gas cost per transaction (very low for testing)
# Price simulation models for different token pairs
spot:
spot_list:
- name: USDC/DAI # Stable coin pair
WGN: # White Gaussian Noise model (for stable assets)
s0: 1.001 # Starting price: $1.001
mean: 1. # Mean revert to $1.00
vol: 0.0008 # Low volatility (0.08%)
- name: USDC/USDT # Another stable pair
WGN:
s0: 1.001
mean: 1.
vol: 0.0008
- name: WETH/DAI # Volatile asset pair
GBM: # Geometric Brownian Motion (for volatile assets)
s0: 2000. # Starting price: $2000
mu: 0.1 # 10% annual drift
vol: 0. # Zero volatility for this example
- name: WETH/WBTC # Cross-asset pair
GBM:
s0: 0.1 # WETH/WBTC ratio
mu: 0.3 # 30% annual drift
vol: 0.
- name: wstETH/WETH # Liquid staking pair
GBM:
s0: 0.1
mu: 0.3
vol: 0.
# Correlation matrix between the 5 price pairs above
correlation:
[
[1., 0.5, 0.5, 0.2, 0.2], # USDC/DAI correlations
[0.5, 1., 0.5, 0.2, 0.2], # USDC/USDT correlations
[0.2, 0.2, 1., 0.5, 0.5], # WETH/DAI correlations
[0.2, 0.2, 0.5, 1., 0.5], # WETH/WBTC correlations
[0.2, 0.2, 0.5, 0.5, 1.], # wstETH/WETH correlations
]
# Historical data and protocol setup
backtest_environment:
# Real Ethereum token contract addresses
token_addresses:
DAI: "0x6b175474e89094c44da98b954eedeac495271d0f" # DAI stablecoin
USDC: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" # USD Coin
USDT: "0xdac17f958d2ee523a2206206994597c13d831ec7" # Tether
WETH: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" # Wrapped Ether
WBTC: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599" # Wrapped Bitcoin
# Protocols and pools to include in simulation
protocols_to_replay:
uniswap_v3:
pools:
- pool_name: univ3_usdc_usdt # USDC/USDT pool
address: "0x3416cf6c708da44db2624d63ea0aaef7113527c6"
- pool_name: univ3_wbtc_eth # WBTC/WETH pool
address: "0xCBCdF9626bC03E24f779434178A73a0B4bad62eD"
# simulation_environment: # Can be used instead of backtest_environment for simulation-specific settings
# DeFi agents and their strategies
agents:
- name: agent_1 # First trading agent
wallet: # Initial portfolio
USDT: 100000 # $100k USDT
USDC: 100000 # $100k USDC
DAI: 100000 # $100k DAI
ETH: 100 # 100 ETH (~$200k at $2000/ETH)
strategy:
continuous_events: # Repeating actions during simulation
- name: event1
block_number: 18725000 # Start at this block
frequency: 100 # Execute every 100 blocks
actions:
- action_name: action1
# Complex condition: execute when ETH < $2000.2 OR DAI/ETH < 1/2000.6
condition: common.market_spot:{"WETH/USDC"} < 2000.2 OR common.market_spot:{"DAI/WETH"} < 1 / 2000.6
protocol_id: univ3_usdc_usdt # Target the USDC/USDT pool
name: mint # Provide liquidity
args:
amount: 10. # Amount to provide
price_lower: 0.5 # Lower price bound
price_upper: 1.5 # Upper price bound
- action_name: action2
# Always execute (no condition)
protocol_id: univ3_wbtc_eth # Target WBTC/ETH pool
name: swap # Perform a swap
args:
amount1_in: 1. # Swap 1 unit of token
Working with Configurations¶
Configurations can be provided in multiple formats:
# From a YAML file
sim = Simulation(protocols, "config.yaml")
# From a JSON file
sim = Simulation(protocols, "config.json")
# From a dictionary
config = {
"simulation": {"blocks": 1000},
"agents": [],
"protocols": {}
}
sim = Simulation(protocols, config)
# From a YAML string
yaml_config = """
simulation:
blocks: 1000
agents: []
protocols: {}
"""
sim = Simulation(protocols, yaml_config)
Running A Configuration¶
To use a configuration in your simulation, simply save it as a YAML file and load it as shown in the Getting Started guide.
from nqs_sdk import Simulation
from nqs_sdk.protocols import UniswapV3
import json
# Initialize simulation with protocols and configuration
uniswap_v3 = UniswapV3Factory() # factory for all UniswapV3 instances
sim = Simulation([uniswap_v3], "your_config.yml")
# Run the simulation
all_observables_str = sim.run() # this intermediate step will be fixed soon
all_observables = json.loads(all_observables_str)
# Access results
print(f"Simulation completed with {len(all_observables)} observables")
This configuration demonstrates advanced features like conditional trading, multi-token portfolios, and realistic market simulation with correlation between assets.
Practical Examples¶
Here are four comprehensive examples demonstrating realistic end-to-end operations with Uniswap V3. Each example shows a different strategy and use case.
Example 1: Basic Liquidity Management¶
Use Case: This strategy is perfect for stablecoin pairs where you want to earn fees with minimal impermanent loss. The tight tick range around $1.00 maximizes fee collection for stable assets.
version: 1.0.0
# Simulation parameters - short 3-block simulation for demonstration
common:
block_number_start: 18725000 # Start at a specific historical block
block_number_end: 18725002 # Run for just 3 blocks
block_step_metrics: 1 # Collect metrics every block
numeraire: USDC # Use USDC as base currency
gas_fee: 10 # Gas cost per transaction
gas_fee_ccy: USDC # Pay gas in USDC
arbitrage_block_frequency: 100 # Low arbitrage frequency
# Price model for USDC/USDT (stable pair)
spot:
spot_list:
- name: USDC/USDT
custom: # Custom price path
timestamps: [1701838079, 1701838091, 1701838103]
path: [1.0, 1.0, 1.0] # Stable $1.00 price
# Protocol and pool setup
simulation_environment:
tokens_info:
USDC: {decimals: 6} # USDC has 6 decimal places
USDT: {decimals: 6} # USDT has 6 decimal places
protocols_to_simulate:
uniswap_v3:
initial_state:
custom_state:
pools:
- pool_name: univ3_usdc_usdt_001
symbol_token0: USDC
symbol_token1: USDT
fee_tier: 0.01 # 0.01% fee tier (1 basis point)
initial_balance:
amount: 100000000 # 100M initial pool liquidity
unit: token0
# Trading agent strategy
agents:
- name: liquidity_provider
wallet:
USDC: 10000 # Start with $10k USDC
USDT: 10000 # Start with $10k USDT
strategy:
timed_events:
# Block 1: Create liquidity position
- name: create_position
block_number: 18725001
actions:
- action_name: mint_liquidity
protocol_id: univ3_usdc_usdt_001
name: raw_mint # Low-level mint operation
args:
tick_lower: -101 # Lower price bound (around $0.99)
tick_upper: 101 # Upper price bound (around $1.01)
amount: 1985301236174 # Precise liquidity amount
token_id: stable_position
# Block 2: Close position
- name: close_position
block_number: 18725002
actions:
- action_name: burn_liquidity
protocol_id: univ3_usdc_usdt_001
name: raw_burn # Remove liquidity
args:
tick_lower: -101
tick_upper: 101
amount: 1985301236174 # Remove all liquidity
collect: true # Automatically collect fees + principal
Example 2: Dynamic Market Making¶
Use Case: Demonstrates the simulator’s power with minimal configuration. Shows realistic price volatility effects on liquidity positions, perfect for understanding impermanent loss vs fee collection dynamics in volatile markets.
version: 1.0.0
# Simulation parameters - 1000 blocks with high volatility price action
common:
block_number_start: 18725000 # Historical Ethereum block number
block_number_end: 18726000 # 1000 blocks simulation (~3-4 hours)
block_step_metrics: 50 # Collect metrics every 50 blocks
numeraire: USDC # Use USDC as base currency for calculations
plot_output: true # Generate performance charts
save_metrics: false # Don't save detailed metrics to file
arbitrage_block_frequency: 5 # Check arbitrage opportunities every 5 blocks
# Price model for ETH/USDC (volatile pair)
spot:
spot_list:
- name: WETH/USDC
gbm: # Geometric Brownian Motion model
s0: 2000 # Starting price: $2000 per ETH
mu: 0. # No drift (neutral market expectation)
vol: 0.4 # 40% annual volatility (high volatility)
correlation: [[1]] # Single asset, perfect self-correlation
# Protocol and pool setup
simulation_environment:
protocols_to_simulate:
uniswap_v3:
initial_state:
custom_state:
pools:
- pool_name: uniswapv3_eth_usdc
symbol_token0: ETH # First token in the pair
symbol_token1: USDC # Second token in the pair
fee_tier: 0.0005 # 0.05% fee tier (5 basis points)
initial_balance:
amount: 10000000 # 10M USDC initial pool liquidity
unit: token1 # Specify liquidity amount in USDC
# Market maker agent strategy
agents:
- name: market_maker
wallet:
USDC: 50000 # Start with $50k USDC
ETH: 25 # Start with 25 ETH (~$50k at $2000)
strategy:
timed_events:
# Create liquidity position at simulation start
- name: initial_position
block_number: 18725001 # Execute on block 1
actions:
- action_name: create_position
protocol_id: uniswapv3_eth_usdc
name: mint # Provide liquidity
args:
amount1: 20000 # Use $20k USDC for liquidity
price_lower: 1800 # Lower price bound ($1800)
price_upper: 2200 # Upper price bound ($2200)
token_id: mm_position # Position identifier