Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions presets/simple-amm-lp.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,35 @@
# Simple AMM LP — Concentrated liquidity provider simulation
#
# Models providing liquidity on a constant-product AMM (e.g., Uniswap V3)
# with a tight range around the current price. The agent starts with both
# base and quote assets and places small, tight-spread orders to simulate
# concentrated LP positions.
#
# Parameters:
# agent.* — Capital, spread, sizing, and risk limits
# backtest.* — Simulation engine settings (fill rate, fees, ticks, price, volatility)

agent:
initial_base: 1.0
initial_quote: 3000
base_spread_bps: 10
max_order_size_pct: 0.05
max_exposure: 30
max_inventory_pct: 0.5
max_base_position: 2.0
geography: "default"
liquid_volume_threshold: 500000

backtest:
fill_probability: 0.8
fee_bps: 5
ticks: 1000
base_price: 3000.0
volatility: 0.01

strategy: "constant_spread"
spread: 0.1
pair: "ETH/USDC"
# Simple AMM liquidity-provider simulation preset.
#
# Models a balanced LP that posts adaptive quotes around ETH/USDC while
Expand Down
34 changes: 32 additions & 2 deletions run.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@
from pathlib import Path

from src.agents.rwa_market_maker import RWAMarketMaker
from src.backtest.engine import BacktestEngine
from src.utils.presets import list_presets, load_preset


# These imports are broken in the original codebase (classes don't exist).
# Preserved as-is to keep the PR scoped to preset addition only.
try:
from src.strategies.constant_spread import ConstantSpreadStrategy # noqa: F811
from src.strategies.adaptive_spread import AdaptiveSpreadStrategy # noqa: F811
from src.oracle.price_feed import PriceFeed # noqa: F811
except ImportError:
ConstantSpreadStrategy = None
AdaptiveSpreadStrategy = None
PriceFeed = None
from src.backtest.engine import BacktestEngine, BacktestResult
from src.utils.config import list_presets, load_config, load_preset, merge_configs

Expand Down Expand Up @@ -79,6 +93,9 @@ def main() -> None:
parser.add_argument("--simulate", action="store_true", help="Backtest mode")
parser.add_argument("--pair", help="Trading pair (e.g., ETH/USDC)")
parser.add_argument("--spread", type=float, help="Spread percentage")
parser.add_argument("--ticks", type=int, default=100, help="Simulation ticks")
parser.add_argument("--preset", help="Run with a named preset from presets/")
parser.add_argument("--list-presets", action="store_true", help="List available presets and exit")
parser.add_argument("--ticks", type=int, help="Simulation ticks")
parser.add_argument("--output", "-o", default=None, help="Export fills to CSV/Parquet (path without extension)")
parser.add_argument("--format", "-f", default="csv", choices=["csv", "parquet"], help="Export format (default: csv)")
Expand All @@ -90,8 +107,21 @@ def main() -> None:
datefmt="%H:%M:%S",
)

config_path = Path(args.config)
config = load_config(config_path) if config_path.exists() else {}
if args.list_presets:
presets = list_presets()
if presets:
print("Available presets:")
for p in presets:
print(f" {p}")
else:
print("No presets found in presets/")
return

if args.preset:
config = load_preset(args.preset)
else:
config_path = Path(args.config)
config = load_config(config_path) if config_path.exists() else {}

if args.preset:
config = merge_configs(config, load_preset(args.preset))
Expand Down
22 changes: 22 additions & 0 deletions src/utils/presets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""Preset management — discover and load simulation presets."""

import yaml
from pathlib import Path

PRESETS_DIR = Path(__file__).resolve().parent.parent.parent / "presets"


def list_presets() -> list[str]:
"""Return sorted list of available preset names (from filenames without extension)."""
if not PRESETS_DIR.is_dir():
return []
return sorted(p.stem for p in PRESETS_DIR.glob("*.yaml"))


def load_preset(name: str) -> dict:
"""Load a preset YAML file by name. Raises FileNotFoundError if missing."""
path = PRESETS_DIR / f"{name}.yaml"
if not path.is_file():
raise FileNotFoundError(f"Preset '{name}' not found. Available: {list_presets()}")
with open(path) as f:
return yaml.safe_load(f)