mts1b_foundation.funds — full reference
The fund object + NAV reporting + capital transfer planning.
FundConfig
class FundConfig(BaseModel):
fund_id: str
name: str
description: str = ""
base_currency: str = "USD"
target_nav_usd: Decimal
broker: str
broker_account_id: str = ""
active: bool = True
asset_classes: list[str] = []
strategies: list[str] = []
tax_lot_method: str = "fifo" # fifo | lifo | hifo | loifo | specid
created_at: datetime
notes: str = ""
Example
from datetime import datetime, timezone
from decimal import Decimal
from mts1b_foundation.funds import FundConfig
fund = FundConfig(
fund_id="paper-momentum",
name="Paper Momentum Demo",
description="Long/short equities, monthly rebalance, 12-1 momentum",
base_currency="USD",
target_nav_usd=Decimal("100000"),
broker="paper",
broker_account_id="paper-acct-1",
active=True,
asset_classes=["equities"],
strategies=["momentum_v3"],
tax_lot_method="hifo",
created_at=datetime.now(timezone.utc),
notes="Demo fund for the docs tutorial",
)
Tax-lot methods
| Method | Effect |
|---|---|
fifo | First-In-First-Out — sell oldest lots first. Simplest. Brokers' default. |
lifo | Last-In-First-Out — sell newest lots first. |
hifo | Highest cost first — minimizes current-year taxable gain (defers taxes). |
loifo | Lowest cost first — harvests highest gains (use to absorb losses). |
specid | Specific identification — operator picks lots per sale. Maximum control. |
See cookbook — tax-lot accounting for the full comparison.
Asset class scoping
asset_classes lists what this fund can trade. mts1b-riskengine enforces:
# If fund_config.asset_classes = ["equities"]:
order = Order(symbol=Symbol("BTC-USD"), ...) # crypto
# → REJECTED: asset_class crypto not in fund's allowed asset_classes
Strategy scoping
strategies lists which strategies can write into this fund. Prevents momentum_v3 from accidentally trading in the carry fund.
NavSnapshot
class NavSnapshot(BaseModel):
fund_id: str
asof: date
nav_usd: Decimal
cash_usd: Decimal
positions_market_value_usd: Decimal
pnl_today_usd: Decimal = Decimal("0")
pnl_today_pct: float = 0.0
pnl_mtd_pct: float = 0.0
pnl_ytd_pct: float = 0.0
return_since_inception: float = 0.0
drawdown_from_peak_pct: float = 0.0
components: dict[str, Decimal] = {}
A daily NAV snapshot. Published to mts.v1.treasury.nav.updated.fund.<fund_id>.
Example
from datetime import date
from mts1b_foundation.funds import NavSnapshot
snap = NavSnapshot(
fund_id="paper-momentum",
asof=date(2026, 5, 23),
nav_usd=Decimal("102400"),
cash_usd=Decimal("2400"),
positions_market_value_usd=Decimal("100000"),
pnl_today_usd=Decimal("450"),
pnl_today_pct=0.0044,
pnl_mtd_pct=0.024,
pnl_ytd_pct=0.124,
return_since_inception=0.124,
drawdown_from_peak_pct=-0.014,
components={
"strategy:momentum_v3": Decimal("450"), # what each strategy contributed today
},
)
Components decomposition
The components dict decomposes today's P/L by source:
components = {
"strategy:momentum_v3": Decimal("450"), # alpha sleeve
"strategy:carry": Decimal("-30"), # another sleeve
"fees_today": Decimal("-12"),
"interest_on_cash": Decimal("0.85"),
}
mts1b-reportslibrary.nav_report uses this to build the daily P/L attribution table.
TransferRequest
class TransferRequest(BaseModel):
transfer_id: str
from_fund: str
to_fund: str
amount_usd: Decimal # gt=0
route: list[dict] = []
cosigned_by: list[str] = []
status: str = "pending" # pending | cosigned | executing | done | canceled
created_at: datetime
Example
from mts1b_foundation.funds import TransferRequest
req = TransferRequest(
transfer_id="xfer-2026-05-23-001",
from_fund="paper-momentum",
to_fund="live-crypto-revol",
amount_usd=Decimal("10000"),
route=[
{"step": 1, "from": "paper-momentum",
"to": "chase-checking", "eta_business_days": 1,
"method": "ACH withdrawal", "fee": "0.00"},
{"step": 2, "from": "chase-checking",
"to": "coinbase", "eta_business_days": 1,
"method": "ACH deposit", "fee": "0.00"},
],
cosigned_by=[],
status="pending",
created_at=datetime.now(timezone.utc),
)
Lifecycle
pending → cosigned → executing → done
↘ canceled
| Status | Means |
|---|---|
pending | Created by allocator; awaiting operator review |
cosigned | Operator 2FA-confirmed; waiting to execute |
executing | Operator has begun moving funds |
done | All legs completed |
canceled | Aborted; no money moved |
Co-sign protocol
mts1b-treasury never moves money on its own. The flow:
- Allocator computes need: "move $10k from fund-A to fund-B"
- Planner builds
route: list[dict]covering bank legs TransferRequestpublished tomts.v1.treasury.transfers.requested- Operator reviews in
mts1b-frontends(webui) - Operator 2FA-confirms;
cosigned_bygets their identity - Operator manually performs each leg (login to broker, withdraw to bank, etc)
- Operator marks each step done in the UI
statusflips todonewhen all legs complete
For internal paper-to-paper moves, transfers are instant (no external action needed). For real-money legs, the human-in-the-loop is required.
Computing fund metrics
def fund_growth_factor(snap: NavSnapshot, initial_nav: Decimal) -> float:
return float(snap.nav_usd / initial_nav)
def cagr(snap: NavSnapshot, initial_nav: Decimal, days_since_inception: int) -> float:
yrs = days_since_inception / 365.25
return float(snap.nav_usd / initial_nav) ** (1 / yrs) - 1
def annualized_drawdown(snap: NavSnapshot) -> float:
"""Worst drawdown × sqrt(252)."""
return snap.drawdown_from_peak_pct * (252 ** 0.5)
Multi-fund attribution
async def firm_pl_today(fund_ids: list[str]) -> Decimal:
"""Sum today's P/L across all active funds."""
snaps = await asyncio.gather(*[
get_nav(fund_id) for fund_id in fund_ids
])
return sum((s.pnl_today_usd for s in snaps), Decimal("0"))
See also
positions— what fills accumulate intorisk—RiskEnvelopereferencesnav_usdfor proportional capsmts1b-treasury— allocator + transfer plannermts1b-reportslibrary— nav_report + settlement_report- Tutorial — Multi-fund treasury