Simulator¶
The fundcloud.sim module is the execution engine: a single Simulator class with four entry points (run_strategy, run_weights, run_signals, run_orders), plus the small protocol classes that govern costs (FixedBps, PerShare, NoCost), slippage (HalfSpread, NoSlippage), and fill timing (NextBarOpen, SameBarClose). All paths return a SimResult that carries the post-run Portfolio, executed Trades, full Order history, and per-bar equity curve. See the Simulator guide for when to pick each entry point.
fundcloud.sim
¶
The simulator engine.
Simulator and SimResult are lazy-loaded to avoid a circular import
against fundcloud.strategies — strategies need Order from this package
and the simulator needs BaseStrategy from fundcloud.strategies.
Simulator
¶
Simulator(
data: Backend | DataFrame,
*,
costs: CostModel | None = None,
slippage: SlippageModel | None = None,
cash: float = 1000000.0,
execution: ExecutionModel | None = None,
)
Discrete-time backtest engine.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
Backend | DataFrame
|
A :class: |
required |
costs
|
CostModel | None
|
Swap-in friction/execution models. Defaults: :class: |
None
|
slippage
|
CostModel | None
|
Swap-in friction/execution models. Defaults: :class: |
None
|
execution
|
CostModel | None
|
Swap-in friction/execution models. Defaults: :class: |
None
|
cash
|
float
|
Starting cash balance. |
1000000.0
|
Examples:
Drive a weekly DCA on a synthetic single-asset Bars frame:
>>> import pandas as pd
>>> from fundcloud.sim import Simulator
>>> from fundcloud.strategies import DCA
>>> bars = pd.DataFrame({ # tiny, flat-price dummy
... ("open", "SPY"): 100.0,
... ("high", "SPY"): 100.0,
... ("low", "SPY"): 100.0,
... ("close", "SPY"): 100.0,
... ("volume", "SPY"): 1_000.0,
... }, index=pd.date_range("2024-01-02", periods=60, freq="B"))
>>> bars.columns = pd.MultiIndex.from_tuples(bars.columns)
>>> result = Simulator(bars, cash=10_000.0).run_strategy(
... DCA(100.0, horizon="weekly", weights={"SPY": 1.0}),
... )
>>> result.equity_curve.iloc[-1] >= 0.0
True
Source code in python/fundcloud/sim/simulator.py
run_orders
¶
Execute an explicit long-format orders DataFrame.
Source code in python/fundcloud/sim/simulator.py
run_signals
¶
Convert boolean entry/exit panels into market orders.
size is a fraction of current cash to allocate per entry.
Source code in python/fundcloud/sim/simulator.py
run_strategy
¶
Drive a :class:BaseStrategy bar by bar and return a :class:SimResult.
The strategy sees one :class:~fundcloud.strategies.base.Context per
bar and returns zero or more :class:~fundcloud.sim.Order instances.
The simulator applies the execution / cost / slippage models and
updates a single live :class:~fundcloud.portfolio.Portfolio.
Source code in python/fundcloud/sim/simulator.py
run_weights
¶
At each row of target_weights, rebalance toward those weights.
target_weights is a dense DataFrame indexed by timestamp with
one column per asset; missing values are forward-filled.
Source code in python/fundcloud/sim/simulator.py
SimResult
dataclass
¶
SimResult(
portfolio: Portfolio,
trades: DataFrame,
orders: DataFrame,
equity_curve: Series,
bars: DataFrame,
)
Output of :meth:Simulator.run_*.
Examples:
>>> # Given ``result = Simulator(bars).run_strategy(strategy)`` —
>>> # ``result.pf`` is a shortcut for ``result.portfolio``:
>>> # result.pf.sharpe()
>>> # result.pf.max_drawdown()
Order
dataclass
¶
Order(
ts: Timestamp,
asset: str,
side: OrderSide,
qty: float | None = None,
notional: float | None = None,
kind: OrderKind = "market",
limit_price: float | None = None,
)
Instruction to trade. Frozen so strategies can hand one up safely.
Exactly one of qty or notional must be set (qty wins if both
are). qty is positive for buys and negative for sells — the
:attr:side field is redundant for market orders but makes limit logic
and audit trails clearer.
Trade
dataclass
¶
Trade(
order: Order,
ts: Timestamp,
asset: str,
qty: float,
price: float,
fee: float = 0.0,
slippage_bps: float = 0.0,
)
A filled :class:Order. The portfolio applies these to mutate state.
Attributes:
| Name | Type | Description |
|---|---|---|
order |
Order
|
The original :class: |
ts |
Timestamp
|
Timestamp at which the fill executed. |
asset |
str
|
Asset being traded (mirrors |
qty |
float
|
Signed quantity. Positive for buys, negative for sells. |
price |
float
|
Fill price after slippage is applied. |
fee |
float
|
Commission / exchange fee charged to cash. |
slippage_bps |
float
|
Slippage applied vs the reference price, in basis points. |
CostModel
¶
Bases: Protocol
Fee charged to cash for a fill.
FixedBps
dataclass
¶
Proportional-to-notional fee, in basis points (1 bp = 0.01 %).
Minimum fee can be set via minimum.
PerShare
dataclass
¶
Flat per-share commission (typical US equity broker pricing).
SlippageModel
¶
HalfSpread
dataclass
¶
Pay half the bid-ask spread (quoted in basis points) on every fill.
ExecutionModel
¶
Bases: Protocol
Decide when an order submitted at bar t fills.
SameBarClose
dataclass
¶
Orders submitted at bar t fill at the close of bar t.
Note the bias: signals derived from bar t can inspect the close then
trade at the close. Use :class:NextBarOpen for honest backtests.