Simulations

The simulations module provides a cashflow simulation framework for trading operations. Before executing a trade, you can calculate exactly which assets will flow in and out of an account — covering purchase costs, fees, PnL, funding payments, borrow interest, and staking rewards.

Balance Tracking

BalanceTracker

BalanceTracker is the central store for asset balances. It separates balances into total (everything owned) and available (free to trade), and adds support for locked balances and derivative positions.

from decimal import Decimal
from financepype.simulations.balances.tracking.tracker import BalanceTracker, BalanceType

tracker = BalanceTracker(track_history=True)  # track_history=False by default

Adding and Removing Balances

from financepype.assets.factory import AssetFactory
from financepype.platforms.platform import Platform

platform = Platform(identifier="sim")
btc = AssetFactory.get_asset(platform, "BTC")

tracker.add_balance(btc, Decimal("1.0"), reason="deposit", balance_type=BalanceType.TOTAL)
tracker.add_balance(btc, Decimal("1.0"), reason="deposit", balance_type=BalanceType.AVAILABLE)

tracker.remove_balance(btc, Decimal("0.1"), reason="withdrawal", balance_type=BalanceType.TOTAL)

Setting Balances

set_balance overwrites an asset's balance and returns a BalanceChange record:

change = tracker.set_balance(btc, Decimal("2.0"), "exchange sync", BalanceType.TOTAL)

set_balances updates multiple assets at once. With complete_update=True, assets not in the list are zeroed out:

usdt = AssetFactory.get_asset(platform, "USDT")
tracker.set_balances(
    new_balances=[(btc, Decimal("1.5")), (usdt, Decimal("50000"))],
    reason="snapshot",
    balance_type=BalanceType.TOTAL,
    complete_update=True,
)

Querying Balances

tracker.get_balance(btc, BalanceType.TOTAL)      # Decimal
tracker.get_balance(btc, BalanceType.AVAILABLE)  # Decimal
tracker.get_unlocked_balance(btc)                 # available minus all locks
tracker.get_locked_balance(btc, "order_001")      # amount locked for purpose
tracker.get_available_locked_balance(btc, "order_001")  # remaining in lock
tracker.get_available_balance(btc, "order_001")   # unlocked + available-in-lock

BalanceLock

BalanceLock reserves a portion of available balance for a specific purpose (e.g., an open order). Multiple locks can coexist per asset.

from financepype.simulations.balances.tracking.lock import BalanceLock

lock = BalanceLock(asset=usdt, amount=Decimal("500"), purpose="order_001")
tracker.lock_balance(lock)

# Use some of the lock (e.g., partial fill)
tracker.use_locked_balance(usdt, "order_001", Decimal("250"))

# Freeze (commit) part of the lock
tracker.freeze_locked_balance(usdt, "order_001", Decimal("250"))

# Release (free) the rest
tracker.release_locked_balance(usdt, "order_001", Decimal("250"))

Atomic Multi-Lock

locks = [
    BalanceLock(asset=btc, amount=Decimal("0.1"), purpose="order_002"),
    BalanceLock(asset=usdt, amount=Decimal("5000"), purpose="order_002"),
]
# All locks succeed atomically, or none are applied
tracker.lock_multiple_balances(locks)

# Simulate without actually locking
can_lock = tracker.simulate_locks(locks)

BalanceType and BalanceUpdateType

from financepype.simulations.balances.tracking.tracker import BalanceType, BalanceUpdateType

# BalanceType
BalanceType.TOTAL      # all owned assets
BalanceType.AVAILABLE  # assets free for trading

# BalanceUpdateType (recorded in history)
BalanceUpdateType.SNAPSHOT      # complete overwrite
BalanceUpdateType.DIFFERENTIAL  # incremental change
BalanceUpdateType.SIMULATED     # hypothetical change

Balance History

If track_history=True, every balance change is recorded:

history = tracker.balance_history  # list[BalanceChange]
for change in history:
    print(change.timestamp, change.asset, change.amount, change.reason)

tracker.clear_balance_history()

Balance Engines

Balance engines simulate the cashflows of trading operations without actually executing them. They answer the question: what will happen to my balances if I do this trade?

BalanceEngine (Abstract)

from financepype.simulations.balances.engines.engine import BalanceEngine

Each engine implements four class methods that return lists of AssetCashflow:

Method Description
get_involved_assets(details) Assets involved (no amounts)
get_opening_outflows(details) Assets leaving at open
get_opening_inflows(details) Assets arriving at open
get_closing_outflows(details) Assets leaving at close
get_closing_inflows(details) Assets arriving at close

get_complete_simulation(details) combines all four into an OperationSimulationResult.

AssetCashflow and Models

from financepype.simulations.balances.engines.models import (
    AssetCashflow, CashflowType, InvolvementType, CashflowReason,
    OperationSimulationResult,
)
Enum Values
CashflowType INFLOW, OUTFLOW
InvolvementType OPENING, CLOSING, SETTLEMENT
CashflowReason OPERATION, FEE, PNL, FUNDING, INTEREST, REWARD, MARGIN, COLLATERAL

Lifecycle Engines (BalanceEngine)

Model position lifecycles with 4 phases (opening outflows/inflows, closing outflows/inflows).

Engine Market Description
SpotBalanceEngine Spot Buy/sell asset, deduct fees
PerpetualBalanceEngine Perpetual Margin, PnL, funding exposure
OptionBalanceEngine Options Premium, settlement
BorrowBalanceEngine Lending Open/close borrow lifecycle (collateral, repayment)
StakingBalanceEngine Staking Open/close staking lifecycle (stake, unstake, rewards)
MultiEngine Any Combine multiple engines
Dashboard Any Aggregate simulation results

Settlement Engines (SettlementEngine)

Model instantaneous recurring events (one payment at a time, with timestamps).

Engine Use Case Description
FundingSettlementEngine Perpetual Single funding payment
InterestSettlementEngine Lending Single interest accrual period
RewardSettlementEngine Staking Single reward distribution epoch
PeriodicSimulator Any Iterates settlement engines over rate schedules

Example: Spot Engine

from financepype.simulations.balances.engines.spot import SpotBalanceEngine

result = SpotBalanceEngine.get_complete_simulation(order)
print(result.opening_outflows)  # e.g., USDT leaving (buy cost)
print(result.closing_inflows)   # e.g., BTC arriving (purchase)

Example: Funding Settlement

from financepype.simulations.balances.engines.periodic import PeriodicSimulator

# Simulate multiple funding payments over a rate schedule
result = PeriodicSimulator.simulate_funding(funding_order_details, rate_schedule)
print(result.total_by_asset)  # Net funding impact across all periods

BalanceSimulationEngine

BalanceSimulationEngine combines a BalanceTracker with one or more balance engines to simulate and apply trade results.

from financepype.simulations.simulation import BalanceSimulationEngine

engine = BalanceSimulationEngine(tracker)
result = engine.simulate_order(order_details)

# Apply results to tracker
engine.apply_simulation_result(result)

MultiEngine

MultiEngine delegates to different engines based on the operation type:

from financepype.simulations.balances.engines.multiengine import MultiEngine

multi = MultiEngine(engines={
    MarketType.SPOT: SpotBalanceEngine,
    MarketType.PERPETUAL: PerpetualBalanceEngine,
    MarketType.CALL_OPTION: OptionBalanceEngine,
})

result = multi.get_complete_simulation(order_details)

Dashboard

Dashboard aggregates simulation results across multiple operations for portfolio-level analysis:

from financepype.simulations.balances.engines.dashboard import Dashboard

dashboard = Dashboard()
dashboard.add_result(result1)
dashboard.add_result(result2)

summary = dashboard.get_summary()
print(summary.net_inflows)
print(summary.total_fees)

Practical Example

Simulate the full lifecycle of a spot buy order:

import asyncio
from decimal import Decimal
from financepype.platforms.platform import Platform
from financepype.assets.factory import AssetFactory
from financepype.markets.trading_pair import TradingPair
from financepype.simulations.balances.tracking.tracker import BalanceTracker, BalanceType
from financepype.simulations.balances.tracking.lock import BalanceLock

platform = Platform(identifier="sim")
btc = AssetFactory.get_asset(platform, "BTC")
usdt = AssetFactory.get_asset(platform, "USDT")

tracker = BalanceTracker()

# Initial state
tracker.set_balance(usdt, Decimal("10000"), "initial", BalanceType.TOTAL)
tracker.set_balance(usdt, Decimal("10000"), "initial", BalanceType.AVAILABLE)

# When order is placed: lock USDT
lock = BalanceLock(asset=usdt, amount=Decimal("500"), purpose="order_001")
tracker.lock_balance(lock)

# When order is filled: release lock, update balances
tracker.release_locked_balance(usdt, "order_001", Decimal("500"))
tracker.remove_balance(usdt, Decimal("500"), "buy fill", BalanceType.TOTAL)
tracker.remove_balance(usdt, Decimal("500"), "buy fill", BalanceType.AVAILABLE)
tracker.add_balance(btc, Decimal("0.01"), "buy fill", BalanceType.TOTAL)
tracker.add_balance(btc, Decimal("0.01"), "buy fill", BalanceType.AVAILABLE)

print(tracker.get_balance(usdt, BalanceType.TOTAL))   # Decimal('9500')
print(tracker.get_balance(btc, BalanceType.TOTAL))    # Decimal('0.01')