Aller au contenu

Aster Perps

Field Value
Module almanak.connectors.aster_perps
Protocol kind Perp
Aliases N/A

Supported Chains And Intents

Chain Family Supported Intents
BNB Chain EVM PERP_CLOSE, PERP_OPEN

API Reference

almanak.connectors.aster_perps

Aster Perps connector (Aster/ApolloX Diamond on BSC).

Aster is the on-chain perpetual trading platform (formerly ApolloX, rebranded March 2025). PancakeSwap Perps runs on top of Aster as broker id = 2; raw Aster use is broker id = 0. The canonical connector lives here; pancakeswap_perps is a thin shim that binds broker_id=2 for backward compatibility.

Phase 1 scope (PRD: docs/internal/discussions/aster-dex-integration-20260418.md): - BSC only - Market orders only - Crypto markets (BTC/USD, ETH/USD, BNB/USD) - No SL/TP, no limit orders - Native BNB margin (openMarketTradeBNB) or ERC20 margin (openMarketTrade)

Multi-chain EVM, spot, Solana, funding-rate data are deferred to later phases gated on named deep-research items (VIB-3044 epic).

Example usage — strategy-author facing::

# Inside an IntentStrategy.decide()
return Intent.perp_open(
    market="BTC/USD",
    collateral_token="BNB",
    collateral_amount=Decimal("0.1"),
    size_usd=Decimal("300"),
    is_long=True,
    max_slippage=Decimal("0.01"),
    protocol="aster_perps",              # canonical key
    leverage=Decimal("3"),
)

# Legacy callers may still pass protocol="pancakeswap_perps"; the compiler
# routes that through the pancakeswap_perps shim, which forces broker_id=2.

SELECTOR_CLOSE_TRADE module-attribute

SELECTOR_CLOSE_TRADE = fromhex('5177fd3b')

closeTrade(bytes32)

SELECTOR_OPEN_MARKET_TRADE module-attribute

SELECTOR_OPEN_MARKET_TRADE = fromhex('703085c7')

openMarketTrade((address,bool,address,uint96,uint80,uint64,uint64,uint64,uint24))

SELECTOR_OPEN_MARKET_TRADE_BNB module-attribute

SELECTOR_OPEN_MARKET_TRADE_BNB = fromhex('b7aeae66')

openMarketTradeBNB((address,bool,address,uint96,uint80,uint64,uint64,uint64,uint24))

AsterPerpsAdapter

AsterPerpsAdapter(config: AsterPerpsConfig)

Translate PERP_OPEN / PERP_CLOSE intents into transaction data.

build_open

build_open(
    *,
    market: str,
    collateral_token: str,
    collateral_amount: Decimal,
    collateral_decimals: int,
    size_usd: Decimal,
    mark_price: Decimal,
    is_long: bool,
    max_slippage: Decimal,
) -> PerpOpenOrderResult

Build an openMarketTrade / openMarketTradeBNB transaction.

参数:

名称 类型 描述 默认
market str

market symbol (e.g. 'BTC/USD'). Must be registered in almanak.core.contracts.ASTER_PERPS_MARKETS[chain].

必需
collateral_token str

token symbol (e.g. 'BNB', 'USDT', 'USDC') or 0x address.

必需
collateral_amount Decimal

margin amount in human decimal terms.

必需
collateral_decimals int

decimals of the margin token (resolver-provided).

必需
size_usd Decimal

position notional in USD.

必需
mark_price Decimal

current oracle mark price (in USD, not scaled) — used to convert the USD size into an 8-decimal qty and to compute the slippage-to-limit-price bound.

必需
is_long bool

True for long.

必需
max_slippage Decimal

fractional slippage tolerance, e.g. Decimal('0.01') for 1%.

必需

返回:

类型 描述
PerpOpenOrderResult

PerpOpenOrderResult. On failure, success=False and error set.

build_close

build_close(*, trade_hash: str | bytes) -> AsterPerpsTx

Build a closeTrade(bytes32) transaction for an open position.

The strategy must supply the tradeHash — PerpCloseIntent's vocabulary keys on (market, is_long, collateral_token) which is insufficient for Aster where multiple positions per market+side are possible. The compiler is responsible for resolving a tradeHash either from intent metadata or from a reader-side lookup (out of v1 scope — strategy stores the hash itself).

AsterPerpsConfig dataclass

AsterPerpsConfig(
    broker_id: int,
    chain: str = "bsc",
    wallet_address: str | None = None,
)

Minimal connector config.

属性:

名称 类型 描述
broker_id int

broker attribution id (REQUIRED — no default). PancakeSwap Perps = 2 (supplied by the pancakeswap_perps shim), raw Aster = 0 (supplied by the compiler for protocol="aster_perps"). Other partner brokers use their own assigned ids.

chain str

chain key (Phase 1 = 'bsc').

wallet_address str | None

trader EOA — not used on-chain by the open call (the router derives user from msg.sender) but recorded on the adapter so the compiler can pass it through to ActionBundle metadata.

AsterPerpsTx dataclass

AsterPerpsTx(
    to: str,
    value: int,
    data: bytes,
    gas_estimate: int,
    description: str,
)

Built transaction for the compiler to wrap in TransactionData.

PerpOpenOrderResult dataclass

PerpOpenOrderResult(
    success: bool,
    error: str | None = None,
    tx: AsterPerpsTx | None = None,
    pair_base: str | None = None,
    margin_token_address: str | None = None,
    qty: int = 0,
    limit_price: int = 0,
    native: bool = False,
    amount_in_wei: int = 0,
)

Adapter output for a compiled PERP_OPEN.

属性:

名称 类型 描述
success bool

False if validation fails before tx construction.

error str | None

description when success is False.

tx AsterPerpsTx | None

the built transaction.

pair_base str | None

resolved pairBase address for the market.

margin_token_address str | None

resolved margin-token address (or NATIVE sentinel).

qty int

computed on-wire qty (uint80, 10-decimal).

limit_price int

computed acceptable limit price (uint64, 8-decimal).

native bool

True if the transaction uses openMarketTradeBNB (value-carrying).

amount_in_wei int

margin amount in token-wei (matches OpenTradeStruct.amountIn).

AsterPerpsReceiptParser

AsterPerpsReceiptParser(chain: str = 'bsc', **_: Any)

Receipt parser for Aster Perps (ApolloX Diamond).

Accept the chain kwarg that ReceiptParserRegistry passes in.

Aster Perps is BSC-only in Phase 1; the chain argument is accepted for registry-interface compatibility and stored for logging/diagnostic use.

parse_receipt

parse_receipt(receipt: dict[str, Any]) -> ParsedReceipt

Decode all Aster-Perps events present in a TX receipt.

Safe to call on receipts with no Aster-Perps events (returns an empty ParsedReceipt). Never raises on malformed logs — logs a warning and skips.

extract_position_id

extract_position_id(receipt: dict[str, Any]) -> str | None

Return the tradeHash from MarketPendingTrade (v1 OPEN path).

If a filled open event (OpenMarketTrade) is present in the same receipt prefer that — but in practice keeper settlement happens in a separate TX, so MarketPendingTrade is the authoritative source for the OPEN-intent return value.

extract_size_delta

extract_size_delta(
    receipt: dict[str, Any],
) -> Decimal | None

Return the qty (position size in base units, 10-decimal — see sdk.QTY_DECIMALS) from the open event.

extract_collateral

extract_collateral(
    receipt: dict[str, Any],
) -> Decimal | None

Return the amountIn (raw margin, in the margin-token's smallest units).

Note: the caller is responsible for applying token decimals — we expose the raw uint96 as a Decimal. (The token's decimal count isn't in the event; it lives in the token registry.)

extract_entry_price

extract_entry_price(
    receipt: dict[str, Any],
) -> Decimal | None

Return the keeper-filled entry price (only present if keeper settlement is in-receipt).

extract_collateral_returned

extract_collateral_returned(
    receipt: dict[str, Any],
) -> Decimal | None

Sum of all payout tokens emitted in CloseTradeReceived events for this receipt.

extract_protocol_fees

extract_protocol_fees(_receipt: dict[str, Any]) -> None

Placeholder for Aster Perps protocol-fee extraction (VIB-3204).

Aster Perps encodes open fee and close fee in on-chain events (OpenMarketTrade.open_fee and CloseTradeSuccessful.close_fee), but translating those raw token amounts to a USD ProtocolFees struct requires token-price context that is not available in the parser. Full extraction is deferred to a follow-up ticket.

extract_funding_fee_usd

extract_funding_fee_usd(_receipt: dict[str, Any]) -> None

No-op stub for funding fee USD extraction (VIB-3520).

Aster Perps emits CloseTradeSuccessful.funding_fee as a raw signed token amount (int96), not a USD value. Converting it to USD requires the collateral token's price at close time, which is not available in the parser. This stub suppresses the extraction warning that ResultEnricher emits when processing PERP_CLOSE intents; a follow-up ticket will implement the full conversion.

CloseTradeReceivedEvent dataclass

CloseTradeReceivedEvent(
    user: str,
    trade_hash: str,
    token: str,
    amount: int,
    log_index: int = 0,
)

TradingCloseFacet.CloseTradeReceived — payout leg of a close.

CloseTradeSuccessfulEvent dataclass

CloseTradeSuccessfulEvent(
    user: str,
    trade_hash: str,
    close_price: int,
    funding_fee: int,
    close_fee: int,
    pnl: int,
    holding_fee: int,
    log_index: int = 0,
)

TradingCloseFacet.CloseTradeSuccessful — fires when keeper fills a close.

MarketPendingTradeEvent dataclass

MarketPendingTradeEvent(
    user: str,
    trade_hash: str,
    pair_base: str,
    is_long: bool,
    token_in: str,
    amount_in: int,
    qty: int,
    price: int,
    stop_loss: int,
    take_profit: int,
    broker: int,
    log_index: int = 0,
)

TradingPortalFacet.MarketPendingTrade — fires on user-signed openMarketTrade.

OpenMarketTradeEvent dataclass

OpenMarketTradeEvent(
    user: str,
    trade_hash: str,
    entry_price: int,
    pair_base: str,
    token_in: str,
    margin: int,
    qty: int,
    is_long: bool,
    open_fee: int,
    execution_fee: int,
    timestamp: int,
    log_index: int = 0,
)

TradingOpenFacet.OpenMarketTrade — fires when keeper fills a pending open.

ParsedReceipt dataclass

ParsedReceipt(
    market_pending_trades: list[
        MarketPendingTradeEvent
    ] = list(),
    open_market_trades: list[OpenMarketTradeEvent] = list(),
    pending_trade_refunds: list[
        PendingTradeRefundEvent
    ] = list(),
    close_trade_successful: list[
        CloseTradeSuccessfulEvent
    ] = list(),
    close_trade_received: list[
        CloseTradeReceivedEvent
    ] = list(),
)

Aggregate view of decoded Aster Perps events in a receipt.

PendingTradeRefundEvent dataclass

PendingTradeRefundEvent(
    user: str,
    trade_hash: str,
    refund_code: int,
    log_index: int = 0,
)

TradingOpenFacet.PendingTradeRefund — fires when keeper refunds a pending trade.

OpenTradeStruct dataclass

OpenTradeStruct(
    pair_base: str,
    is_long: bool,
    token_in: str,
    amount_in: int,
    qty: int,
    price: int,
    broker: int,
    stop_loss: int = 0,
    take_profit: int = 0,
)

Python mirror of the Aster openMarketTrade input struct.

All integer fields use the on-wire units the contract expects
  • amountIn: collateral-token smallest units (wei-equivalent)
  • qty: 10-decimal fixed-point (e.g. 0.15 BTC = 1500000000)
  • price: 8-decimal fixed-point limit / acceptable price
  • stopLoss: 8-decimal fixed-point (0 = no SL)
  • takeProfit: 8-decimal fixed-point (0 = no TP)
  • broker: uint24 broker id (PancakeSwap = 2, raw Aster = 0)

build_close_transaction

build_close_transaction(
    *,
    trade_hash: str | bytes,
    broker_id: int = ASTER_BROKER_RAW,
    chain: str = "bsc",
    wallet_address: str | None = None,
) -> AsterPerpsTx

Build a close transaction without constructing the adapter explicitly.

The close path does not emit a broker-attributed fee; the broker_id is plumbed through for consistency and defaulted to ASTER_BROKER_RAW (0).

build_open_transaction

build_open_transaction(
    *,
    broker_id: int,
    chain: str = "bsc",
    wallet_address: str | None = None,
    **open_kwargs: Any,
) -> PerpOpenOrderResult

Build an open transaction without constructing the adapter explicitly.

参数:

名称 类型 描述 默认
broker_id int

REQUIRED — no default. Pass ASTER_BROKER_RAW (0) for raw Aster use or PANCAKESWAP_PERPS_BROKER_ID (2) for the PancakeSwap Perps attribution path.

必需

encode_close_trade_calldata

encode_close_trade_calldata(
    trade_hash: str | bytes,
) -> bytes

Encode calldata for closeTrade(bytes32).

参数:

名称 类型 描述 默认
trade_hash str | bytes

32-byte position identifier (hex string or raw bytes).

必需

返回:

类型 描述
bytes

4-byte selector + abi-encoded bytes32.

encode_get_pending_trade_calldata

encode_get_pending_trade_calldata(
    trade_hash: str | bytes,
) -> bytes

Encode calldata for the getPendingTrade(bytes32) view.

encode_get_position_by_hash_calldata

encode_get_position_by_hash_calldata(
    trade_hash: str | bytes,
) -> bytes

Encode calldata for the getPositionByHashV2(bytes32) view.

encode_open_market_trade_calldata

encode_open_market_trade_calldata(
    trade: OpenTradeStruct, *, native: bool = False
) -> bytes

Encode calldata for openMarketTrade or openMarketTradeBNB.

参数:

名称 类型 描述 默认
trade OpenTradeStruct

populated OpenTradeStruct

必需
native bool

when True uses openMarketTradeBNB (native BNB margin via msg.value), when False uses openMarketTrade (ERC20 margin, requires prior approve).

False

返回:

类型 描述
bytes

4-byte selector + abi-encoded tuple.

get_margin_token_address

get_margin_token_address(
    symbol: str, chain: str = "bsc", token_resolver=None
) -> str

Resolve a margin-token symbol (WBNB/USDT/USDC) to its BSC ERC-20 address.

Accepts either a symbol or a 0x-prefixed EVM address (passed through after validation). For native BNB, pass symbol='BNB' or 'NATIVE' and the NATIVE_BNB_ADDRESS sentinel is returned (the caller must use encode_open_market_trade_calldata(native=True)).

Symbol resolution goes through the framework's unified :func:almanak.framework.data.tokens.get_token_resolver so the connector stays in sync with the rest of the token-metadata surface (aliases, on-chain fallbacks, disk cache). The local :data:ASTER_PERPS_TOKENS allowlist is only consulted to reject tokens that the Aster router does not accept as margin; the address for allowed symbols comes from the resolver.

参数:

名称 类型 描述 默认
symbol str

Margin token symbol (e.g. 'WBNB', 'USDT', 'USDC', 'BNB', 'NATIVE') or a 0x-prefixed EVM address.

必需
chain str

Chain key (default 'bsc').

'bsc'
token_resolver

Optional TokenResolver override; defaults to the singleton returned by get_token_resolver().

None

引发:

类型 描述
ValueError

If the symbol is not one of Aster Perps' supported margin tokens.

TokenNotFoundError

If the resolver cannot resolve an allowed symbol to an on-chain address on chain (propagated unchanged).

get_pair_base

get_pair_base(market: str, chain: str = 'bsc') -> str

Resolve a market symbol (e.g. 'BTC/USD') to the on-chain pairBase address.

Accepts either a registered symbol (v1: BTC/USD, ETH/USD, BNB/USD) or a 0x-prefixed EVM address (passed through after validation — lets PerpOpenIntent.market carry the pairBase address directly for synthetic / non-registered markets).

Raises ValueError if the symbol is not registered and is not a valid address. Non-crypto markets (NVDA, TSLA, ...) use synthetic ApolloX-issued pairBases and are deferred to v2 per the design doc; callers wanting to hit them today must pass the pairBase address explicitly.

get_router_address

get_router_address(chain: str = 'bsc') -> str

Return the Aster Perps router address for the given chain.

slippage_to_limit_price

slippage_to_limit_price(
    mark_price: Decimal, slippage: Decimal, *, is_long: bool
) -> int

Convert (mark_price, slippage_fraction) into the price field the router expects.

For longs: acceptable price = mark_price * (1 + slippage) — trader willing to pay up to this. For shorts: acceptable price = mark_price * (1 - slippage) — trader willing to receive down to this.

Returned value is uint64 with 8-decimal scaling.

usd_size_to_qty

usd_size_to_qty(
    size_usd: Decimal, mark_price: Decimal
) -> int

Convert a USD notional to on-wire qty (uint80, 10-decimal fixed-point).

qty = size_usd / mark_price, then scale to 1e10 (see QTY_DECIMALS).

Raises ValueError on non-positive inputs (keeps the connector fail-fast — silently coercing zero/negative sizes into bogus positions is exactly the class of bug the 'no quick patches' guardrail forbids).

__getattr__

__getattr__(name: str) -> Any

PEP 562 lazy attribute access.