v1.0.2

Portfolio State

Kafal features a high-performance multi-asset execution engine (PortfolioState) capable of orchestrating per-symbol position tracking, trade ledgers, and aggregated portfolio equity curves.

Multi-Asset Architecture

While StrategyState handles execution for a single instrument, PortfolioState wraps multiple per-symbol engines into a unified portfolio:

  • Isolated Ledgers: Each asset maintains its own order book and open position lots.
  • Vectorized Aggregation: When finalizing, Kafal uses Pandas C-level operations to combine equity curves across all assets instantly.
  • Staggered Protection: Leading NaNs (e.g., from assets with late historical start dates) are safely padded with initial equity to prevent index misalignment.

The DSL portfolio.* API

Inside a Kafal script, multi-symbol trading signals are dispatched using the portfolio object. Every call takes a symbol identifier as its first argument.

// Multi-Symbol Strategy Logic in Kafal
fast_a = ta.sma(close, 10)
slow_a = ta.sma(close, 30)

long_cond = crossover(fast_a, slow_a)
short_cond = crossunder(fast_a, slow_a)

// Route orders specifically to individual assets
portfolio.long("AAPL", long_cond, size=10.0)
portfolio.short("AAPL", short_cond, size=10.0)

// Place bracket orders on another symbol
portfolio.bracket_long("MSFT", entry_price=200.0, tp_price=220.0, sl_price=190.0, size=5.0)

Python Host Integration

Host applications can directly instantiate and step through a PortfolioState engine from Python:

import pandas as pd
from kafal.core.portfolio_state import PortfolioState

symbol_prices = {
    "AAPL": df_aapl["close"],
    "MSFT": df_msft["close"]
}

ohlc_map = {
    "AAPL": {"open": df_aapl["open"], "high": df_aapl["high"], "low": df_aapl["low"], "close": df_aapl["close"]},
    "MSFT": {"open": df_msft["open"], "high": df_msft["high"], "low": df_msft["low"], "close": df_msft["close"]}
}

pf = PortfolioState(
    symbol_prices=symbol_prices,
    ohlc_map=ohlc_map,
    initial_equity=100_000.0,
    commission_perc=0.0005,
    execution_mode="realistic"
)

for i in range(len(df_aapl)):
    pf.mark(i)

results = pf.finalize()
print("Portfolio Net Profit:", results["stats"]["net_profit"])