#!/usr/bin/env python3
"""Synthetic margin replay; stdlib only, no market data or exchange connection.

Assumptions: one USD-settled linear long, constant maintenance rate, discrete
mark-price events, full close at the first failing event's price, zero costs,
no re-entry. Transfers move existing reserve cash, never add investor wealth.
This intentionally does not implement any exchange's liquidation procedure.
"""

from dataclasses import dataclass
from decimal import Decimal as D


@dataclass(frozen=True)
class Result:
    liquidated: bool
    minimum_buffer: D
    final_wealth: D
    unconstrained_drawdown: D
    realized_drawdown: D


def drawdown(values):
    peak = values[0]
    worst = D(0)
    for value in values:
        peak = max(peak, value)
        worst = max(worst, (peak - value) / peak)
    return worst


def replay(low, transfers=None, collateral=D(1200)):
    prices = list(map(D, [100, 100, low, 101, 103]))
    transfers = transfers or {}
    reserve = D(10000) - collateral
    quantity = D(100)
    entry = D(100)
    rate = D("0.05")
    liquidated = False
    buffers, wealth, unconstrained = [], [], []

    for event, price in enumerate(prices):
        transfer = D(transfers.get(event, 0))
        assert D(0) <= transfer <= reserve
        before = collateral + reserve
        collateral += transfer
        reserve -= transfer
        assert collateral + reserve == before

        equity = collateral + quantity * (price - entry)
        if quantity:
            maintenance = rate * quantity * price
            buffer = equity - maintenance
            buffers.append(buffer)
            if buffer <= 0:
                collateral = equity  # Realize P&L at this event's price.
                quantity = D(0)
                liquidated = True
        wealth.append(reserve + equity)
        unconstrained.append(D(10000) + D(100) * (price - entry))

    return Result(liquidated, min(buffers), wealth[-1],
                  drawdown(unconstrained), drawdown(wealth))


def main():
    cases = {
        "mild": replay(95),
        "shock": replay(92),
        "transfer_before": replay(92, {1: 300}),
        "transfer_after": replay(92, {3: 300}),
    }
    assert cases["mild"] == Result(False, D(225), D(10300), D(".05"), D(".05"))
    assert cases["shock"] == Result(True, D(-60), D(9200), D(".08"), D(".08"))
    assert cases["transfer_before"] == Result(False, D(240), D(10300), D(".08"), D(".08"))
    assert cases["transfer_after"] == cases["shock"]
    assert replay(100, collateral=D(500)).liquidated  # Equality triggers.
    assert drawdown(list(map(D, [10000, 10100, 10300]))) == 0
    trigger = (D(100) * D(100) - D(1200)) / (D(100) * (1 - D(".05")))
    print(f"synthetic_trigger_price_usd={trigger:.6f}")
    print("unconstrained_close_only_maxdd=0.00%")
    print("case,liquidated,min_buffer_usd,final_wealth_usd,unconstrained_maxdd,realized_maxdd")
    for name, result in cases.items():
        print(f"{name},{str(result.liquidated).lower()},"
              f"{result.minimum_buffer:.2f},{result.final_wealth:.2f},"
              f"{100 * result.unconstrained_drawdown:.2f}%,"
              f"{100 * result.realized_drawdown:.2f}%")
    print("invariants=PASS")


if __name__ == "__main__":
    main()
