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
¶
closeTrade(bytes32)
SELECTOR_OPEN_MARKET_TRADE
module-attribute
¶
openMarketTrade((address,bool,address,uint96,uint80,uint64,uint64,uint64,uint24))
SELECTOR_OPEN_MARKET_TRADE_BNB
module-attribute
¶
openMarketTradeBNB((address,bool,address,uint96,uint80,uint64,uint64,uint64,uint24))
AsterPerpsAdapter
¶
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 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
¶
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
¶
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
¶
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
¶
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
¶
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
¶
Return the qty (position size in base units, 10-decimal — see sdk.QTY_DECIMALS) from the open event.
extract_collateral
¶
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
¶
Return the keeper-filled entry price (only present if keeper settlement is in-receipt).
extract_collateral_returned
¶
Sum of all payout tokens emitted in CloseTradeReceived events for this receipt.
extract_protocol_fees
¶
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
¶
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
¶
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
¶
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 |
必需 |
encode_close_trade_calldata
¶
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 calldata for the getPendingTrade(bytes32) view.
encode_get_position_by_hash_calldata
¶
Encode calldata for the getPositionByHashV2(bytes32) view.
encode_open_market_trade_calldata
¶
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
¶
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 |
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 |
get_pair_base
¶
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
¶
Return the Aster Perps router address for the given chain.
slippage_to_limit_price
¶
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
¶
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).