mirror of
https://github.com/manifoldbt/manifoldbt.git
synced 2026-08-24 14:38:04 +00:00
release: v0.5.0
This commit is contained in:
@@ -34,6 +34,7 @@ from manifoldbt.config import (
|
||||
ExecutionConfig,
|
||||
FeeConfig,
|
||||
OrderConfig,
|
||||
VenueFees,
|
||||
resolve_universe,
|
||||
)
|
||||
from manifoldbt.exceptions import (
|
||||
@@ -110,6 +111,11 @@ import atexit
|
||||
atexit.register(_print_pro_summary)
|
||||
|
||||
|
||||
def license_info() -> tuple:
|
||||
"""Get license info: (tier, email). tier is "Pro" or "Community", email is str or None."""
|
||||
return _license_info()
|
||||
|
||||
|
||||
def _is_pro() -> bool:
|
||||
"""Check if current license is Pro."""
|
||||
try:
|
||||
@@ -270,6 +276,22 @@ def _prepare_config(config: BacktestConfig, strategy, store: DataStore) -> Backt
|
||||
if cfg.provider and not cfg.signal_source:
|
||||
cfg.signal_source = cfg.provider
|
||||
|
||||
# --- Resolve per-venue fee mapping: symbol_venue keys may be symbol names ---
|
||||
# Users key symbol_venue by name (e.g. "dydx:BTC-USD:perp" or "BTC-USDT:perp")
|
||||
# for ergonomics; the engine needs integer SymbolIds. Resolve them here using
|
||||
# the same name→id mapping as the universe.
|
||||
fees = getattr(cfg, "fees", None)
|
||||
if fees is not None and getattr(fees, "symbol_venue", None):
|
||||
resolved_sv = {}
|
||||
for key, venue in fees.symbol_venue.items():
|
||||
if isinstance(key, int):
|
||||
resolved_sv[key] = venue
|
||||
elif cfg.symbol_names and key in cfg.symbol_names:
|
||||
resolved_sv[int(cfg.symbol_names[key])] = venue
|
||||
else:
|
||||
resolved_sv[int(store.resolve_symbol(key))] = venue
|
||||
fees.symbol_venue = resolved_sv
|
||||
|
||||
# Merge orders from strategy into execution config
|
||||
if strategy and hasattr(strategy, '_orders') and strategy._orders:
|
||||
if cfg.execution.orders is None:
|
||||
@@ -1140,6 +1162,7 @@ __all__ = [
|
||||
"BacktestConfig",
|
||||
"ExecutionConfig",
|
||||
"FeeConfig",
|
||||
"VenueFees",
|
||||
"OrderConfig",
|
||||
# Helpers
|
||||
"date_to_ns",
|
||||
|
||||
@@ -103,7 +103,13 @@ class ExecutionConfig:
|
||||
|
||||
|
||||
@dataclass
|
||||
class FeeConfig:
|
||||
class VenueFees:
|
||||
"""Fee schedule for a single venue (exchange).
|
||||
|
||||
The same fields as a flat (single-venue) :class:`FeeConfig`. Used as the
|
||||
value type of :attr:`FeeConfig.per_venue` to express per-exchange fees.
|
||||
"""
|
||||
|
||||
maker_fee_bps: float = 0.0
|
||||
taker_fee_bps: float = 0.0
|
||||
funding_rate_column: Optional[str] = None
|
||||
@@ -122,6 +128,79 @@ class FeeConfig:
|
||||
"default_fill_type": self.default_fill_type,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class FeeConfig:
|
||||
"""Transaction-cost configuration.
|
||||
|
||||
The flat fields below describe the *default* venue, applied to any symbol
|
||||
not present in ``symbol_venue``. Per-venue fees are opt-in via ``per_venue``
|
||||
(named fee schedules) plus ``symbol_venue`` (which symbol trades where).
|
||||
Single-venue configs are unchanged — leaving ``per_venue``/``symbol_venue``
|
||||
empty serializes to the exact same JSON as before.
|
||||
"""
|
||||
|
||||
maker_fee_bps: float = 0.0
|
||||
taker_fee_bps: float = 0.0
|
||||
funding_rate_column: Optional[str] = None
|
||||
borrow_rate_annual_bps: float = 0.0
|
||||
min_fee: float = 0.0
|
||||
default_fill_type: str = "Taker"
|
||||
"""Default fill type for fee calculation: "Maker" or "Taker" (conservative)."""
|
||||
per_venue: Dict[str, VenueFees] = field(default_factory=dict)
|
||||
"""Named per-venue fee overrides, keyed by venue name (e.g. ``"binance"``)."""
|
||||
symbol_venue: Dict[int, str] = field(default_factory=dict)
|
||||
"""Maps a ``SymbolId`` (integer) to the name of the venue it executes on.
|
||||
Symbols absent from this map use the default-venue fields above."""
|
||||
|
||||
def to_json_dict(self) -> dict:
|
||||
d: dict = {
|
||||
"maker_fee_bps": self.maker_fee_bps,
|
||||
"taker_fee_bps": self.taker_fee_bps,
|
||||
"funding_rate_column": self.funding_rate_column,
|
||||
"borrow_rate_annual_bps": self.borrow_rate_annual_bps,
|
||||
"min_fee": self.min_fee,
|
||||
"default_fill_type": self.default_fill_type,
|
||||
}
|
||||
# Emit per-venue keys only when populated so single-venue configs stay
|
||||
# byte-identical to the legacy flat shape (matches Rust serde flatten).
|
||||
if self.per_venue:
|
||||
d["per_venue"] = {
|
||||
name: (v.to_json_dict() if isinstance(v, VenueFees) else dict(v))
|
||||
for name, v in self.per_venue.items()
|
||||
}
|
||||
if self.symbol_venue:
|
||||
d["symbol_venue"] = {
|
||||
str(sid): name for sid, name in self.symbol_venue.items()
|
||||
}
|
||||
return d
|
||||
|
||||
@classmethod
|
||||
def multi_venue(
|
||||
cls,
|
||||
default: Optional[VenueFees] = None,
|
||||
venues: Optional[Dict[str, VenueFees]] = None,
|
||||
symbol_venue: Optional[Dict[int, str]] = None,
|
||||
) -> "FeeConfig":
|
||||
"""Build a per-venue fee config.
|
||||
|
||||
Args:
|
||||
default: Fee schedule for symbols without a venue mapping.
|
||||
venues: Named per-venue fee schedules (e.g. ``{"binance": VenueFees(...)}``).
|
||||
symbol_venue: Maps integer ``SymbolId`` to a venue name in ``venues``.
|
||||
"""
|
||||
d = default or VenueFees()
|
||||
return cls(
|
||||
maker_fee_bps=d.maker_fee_bps,
|
||||
taker_fee_bps=d.taker_fee_bps,
|
||||
funding_rate_column=d.funding_rate_column,
|
||||
borrow_rate_annual_bps=d.borrow_rate_annual_bps,
|
||||
min_fee=d.min_fee,
|
||||
default_fill_type=d.default_fill_type,
|
||||
per_venue=venues or {},
|
||||
symbol_venue=symbol_venue or {},
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def binance_perps(cls) -> "FeeConfig":
|
||||
"""Binance USDM perpetual futures defaults (taker fees + funding)."""
|
||||
|
||||
Reference in New Issue
Block a user