Skip to main content

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

MethodEffect
fifoFirst-In-First-Out — sell oldest lots first. Simplest. Brokers' default.
lifoLast-In-First-Out — sell newest lots first.
hifoHighest cost first — minimizes current-year taxable gain (defers taxes).
loifoLowest cost first — harvests highest gains (use to absorb losses).
specidSpecific 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.

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
StatusMeans
pendingCreated by allocator; awaiting operator review
cosignedOperator 2FA-confirmed; waiting to execute
executingOperator has begun moving funds
doneAll legs completed
canceledAborted; no money moved

Co-sign protocol

mts1b-treasury never moves money on its own. The flow:

  1. Allocator computes need: "move $10k from fund-A to fund-B"
  2. Planner builds route: list[dict] covering bank legs
  3. TransferRequest published to mts.v1.treasury.transfers.requested
  4. Operator reviews in mts1b-frontends (webui)
  5. Operator 2FA-confirms; cosigned_by gets their identity
  6. Operator manually performs each leg (login to broker, withdraw to bank, etc)
  7. Operator marks each step done in the UI
  8. status flips to done when 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