Change Log
2024-08-06
- Added support for the following messages in the chain "exchange" module:
- MsgDecreasePositionMargin
- MsgUpdateSpotMarket
- MsgUpdateDerivativeMarket
- MsgAuthorizeStakeGrants
- MsgActivateStakeGrantmodule
- Python SDK v1.6.1: add min_notional to all market classes
- Go SDK v1.51.1: add min_notional to all market structs
2024-07-30
- Updated requests and responses messages with parameters added in chain upgrade to v1.13
- Updated the API documentation to include all queries and messages for the
tendermint
module - Updated the API documentation to include all queries and messages for the
IBC transfer
module - Updated the API documentation to include all queries and messages for the
IBC core channel
module - Updated the API documentation to include all queries and messages for the
IBC core client
module - Updated the API documentation to include all queries and messages for the
IBC core connection
module - Updated the API documentation to include all queries and messages for the
permissions
module - Python SDK v1.6.0
- Added support for all queries from the
tendermint
module - Added support for all queries from the
IBC transfer
module - Added support for all queries from the
IBC core channel
module - Added support for all queries from the
IBC core client
module - Added support for all queries from the
IBC core connection
module - Added support for all queries from the
permissions
module
- Added support for all queries from the
2024-03-08
- Updated the API documentation to include all queries and messages for the
distribution
andchain exchange
modules - Python SDK v1.4.0
- Added support for all queries and messages from the
distribution
module - Added support for all queries and messages from the
chain exchange
module
- Added support for all queries and messages from the
2024-01-25
- Python SDK v1.2.0
- Improved message based gas estimator to consider that Post Only orders creation require more gas than normal orders
2024-01-02
- Python SDK v1.0 and Go SDK v1.49
- Added logic to support use of Client Order ID (CID) new identifier in OrderInfo
- New chain stream support
- Remove support for
sentry
nodes in mainnet network. The only supported node option in mainnet islb
- Migrated all proto objects dependency to support chain version 1.12
- Added logic to cover all bank module queries
- Added logic in Python SDK to support the initialization of tokens with all the tokens from the chain (DenomsMetadata)
- Added logic in Go SDK to allow the initialization of markets and tokens from the chain (without using the local .ini files). Also included functionality to initialize the tokens wilh all the tokens from the chain (DenomsMetadata)
- Added support for wasm, tokenfactory and wasmx modules, including example script for all their endpoints
2023-09-06
- Python SDK v0.8 release
- Network class was moved from
pyinjective.constant
topyinjective.core.network
- Configuration to use secure or insecure channels has been moved into the Network class
- The Composer can be created now by the AsyncClient, taking markets and tokens from the chain information instead of using the local configuration files
- Changed the cookies management logic. All cookies management is done now by Network
- Network class was moved from
2023-08-28
- Added IP rate limits documentation
Introduction
Welcome to Injective's documentation!
Here you can find a comprehensive overview of our protocol, as well as tutorials, guides and general resources for developers and API traders.
If you would like to ask any questions or be a part of our community, please join our Discord Group or Telegram Group. We have a dedicated channel in our Discord group for questions related to the API.
Clients
Python Client
Dependencies
Ubuntu
sudo apt install python3.X-dev autoconf automake build-essential libffi-dev libtool pkg-config
Fedora
sudo dnf install python3-devel autoconf automake gcc gcc-c++ libffi-devel libtool make pkgconfig
macOS
brew install autoconf automake libtool
Installation
Install injective-py from PyPI using pip
.
pip install injective-py
Reference
Markets and Tokens information
Example - Traditional Composer instantiation
from pyinjective.composer import Composer
from pyinjective.transaction import Transaction
from pyinjective.core.network import Network
network = Network.testnet()
composer = Composer(network=network.string())
Python SDK traditionally relied on local configuration files to get the list of available markets and tokens in each network (mainnet, testnet and devnet).
Since version 0.8 the SDK is able also to get the markets and tokens information directly from the chain data (through the Indexer process). The benefit of this approach is that it is not necessary to update the SDK version when a new market is created in the chain or a new token is added.
- To use the markets and tokens information from the local configuration files, create the Composer instance in the traditional way
Example - Get the composer instance through the AsyncClient
from pyinjective.composer import Composer as ProtoMsgComposer
from pyinjective.async_client import AsyncClient
from pyinjective.transaction import Transaction
from pyinjective.core.network import Network
network = Network.testnet()
client = AsyncClient(network)
composer = await client.composer()
- To get the markets and tokens information directly from the chain, create the Composer instance through the AsyncClient
By default the AsyncClient and the Composer will only initialize the tokens that are part of an active market. In order to let them use any of the tokens available in the chain, the user has to execute the initialize_tokens_from_chain_denoms
in the AsyncClient before the creation of the Composer.
Example - Initialize with all tokens from the chain
from pyinjective.composer import Composer as ProtoMsgComposer
from pyinjective.async_client import AsyncClient
from pyinjective.transaction import Transaction
from pyinjective.core.network import Network
network = Network.testnet()
client = AsyncClient(network)
await client.initialize_tokens_from_chain_denoms()
composer = await client.composer()
Golang Client
1. Create your own client repo and go.mod file
go mod init foo
2. Import SDK into go.mod
module foo
go 1.18
require ( github.com/InjectiveLabs/sdk-go v1.39.4 )
Consult the sdk-go repository to find the latest release and replace the version in your go.mod file. Version v1.39.4 is only an example and must be replaced with the newest release
3. Download the package
Download the package using go mod download
go mod download github.com/InjectiveLabs/sdk-go
Markets and Tokens information
Example - Traditional ChainClient instantiation
package main
import (
"fmt"
"github.com/InjectiveLabs/sdk-go/client"
"os"
"github.com/InjectiveLabs/sdk-go/client/common"
chainclient "github.com/InjectiveLabs/sdk-go/client/chain"
rpchttp "github.com/cometbft/cometbft/rpc/client/http"
)
func main() {
network := common.LoadNetwork("testnet", "lb")
tmClient, err := rpchttp.New(network.TmEndpoint, "/websocket")
if err != nil {
panic(err)
}
senderAddress, cosmosKeyring, err := chainclient.InitCosmosKeyring(
os.Getenv("HOME")+"/.injectived",
"injectived",
"file",
"inj-user",
"12345678",
"5d386fbdbf11f1141010f81a46b40f94887367562bd33b452bbaa6ce1cd1381e", // keyring will be used if pk not provided
false,
)
if err != nil {
panic(err)
}
// initialize grpc client
clientCtx, err := chainclient.NewClientContext(
network.ChainId,
senderAddress.String(),
cosmosKeyring,
)
if err != nil {
fmt.Println(err)
}
clientCtx = clientCtx.WithNodeURI(network.TmEndpoint).WithClient(tmClient)
chainClient, err := chainclient.NewChainClient(
clientCtx,
network,
common.OptionGasPrices(client.DefaultGasPriceWithDenom),
)
if err != nil {
fmt.Println(err)
}
}
Go SDK traditionally relied on local configuration files to get the list of available markets and tokens in each network (mainnet, testnet and devnet).
Since version 1.49 the SDK is able also to get the markets and tokens information directly from the chain data (through the Indexer process). The benefit of this approach is that it is not necessary to update the SDK version when a new market is created in the chain or a new token is added.
Example - Get markets and tokens from Indexer (ExchangeClient)
package main
import (
"context"
"github.com/InjectiveLabs/sdk-go/client"
"github.com/InjectiveLabs/sdk-go/client/core"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
"os"
"github.com/InjectiveLabs/sdk-go/client/common"
chainclient "github.com/InjectiveLabs/sdk-go/client/chain"
rpchttp "github.com/cometbft/cometbft/rpc/client/http"
)
func main() {
network := common.LoadNetwork("testnet", "lb")
tmClient, err := rpchttp.New(network.TmEndpoint, "/websocket")
if err != nil {
panic(err)
}
senderAddress, cosmosKeyring, err := chainclient.InitCosmosKeyring(
os.Getenv("HOME")+"/.injectived",
"injectived",
"file",
"inj-user",
"12345678",
"5d386fbdbf11f1141010f81a46b40f94887367562bd33b452bbaa6ce1cd1381e", // keyring will be used if pk not provided
false,
)
if err != nil {
panic(err)
}
// initialize grpc client
clientCtx, err := chainclient.NewClientContext(
network.ChainId,
senderAddress.String(),
cosmosKeyring,
)
if err != nil {
panic(err)
}
clientCtx = clientCtx.WithNodeURI(network.TmEndpoint).WithClient(tmClient)
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketsAssistant, err := core.NewMarketsAssistantUsingExchangeClient(ctx, exchangeClient)
if err != nil {
panic(err)
}
}
- To get the markets and tokens information directly from the chain, create an instance of MarketsAssistant using the
NewMarketsAssistantUsingExchangeClient
function and passing the ExchangeClient as parameter
Example - MarketsAssistant with all tokens
package main
import (
"context"
"github.com/InjectiveLabs/sdk-go/client"
"github.com/InjectiveLabs/sdk-go/client/core"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
"os"
"github.com/InjectiveLabs/sdk-go/client/common"
chainclient "github.com/InjectiveLabs/sdk-go/client/chain"
rpchttp "github.com/cometbft/cometbft/rpc/client/http"
)
func main() {
network := common.LoadNetwork("testnet", "lb")
tmClient, err := rpchttp.New(network.TmEndpoint, "/websocket")
if err != nil {
panic(err)
}
senderAddress, cosmosKeyring, err := chainclient.InitCosmosKeyring(
os.Getenv("HOME")+"/.injectived",
"injectived",
"file",
"inj-user",
"12345678",
"5d386fbdbf11f1141010f81a46b40f94887367562bd33b452bbaa6ce1cd1381e", // keyring will be used if pk not provided
false,
)
if err != nil {
panic(err)
}
// initialize grpc client
clientCtx, err := chainclient.NewClientContext(
network.ChainId,
senderAddress.String(),
cosmosKeyring,
)
if err != nil {
panic(err)
}
clientCtx = clientCtx.WithNodeURI(network.TmEndpoint).WithClient(tmClient)
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
chainClient, err := chainclient.NewChainClient(
clientCtx,
network,
common.OptionGasPrices(client.DefaultGasPriceWithDenom),
)
if err != nil {
panic(err)
}
ctx := context.Background()
marketsAssistant, err := core.NewMarketsAssistantWithAllTokens(ctx, exchangeClient, chainClient)
if err != nil {
panic(err)
}
}
By default the MarketsAssistant will only initialize the tokens that are part of an active market. In order to let it use any of the tokens available in the chain, the user has to create the MarketsAssistant instance using the function NewMarketsAssistantWithAllTokens
.
The MarketsAssistant instance can be used with the following ChainClient functions:
CreateSpotOrder
CreateDerivativeOrder
Reference
Typescript Client
Installation
Install the @injectivelabs/sdk-ts
npm package using yarn
yarn add @injectivelabs/sdk-ts
Reference
To see Typescript examples please check the Typescript SDK documentation page listed above
For other languages
Currently Injective provides SDKs only for Go, Python and TypeScript. To interact with the nodes using a different language please connect directly using the gRPC proto objects. The compiled proto files for C++, C# and Rust can be found in InjectiveLabs/injective-proto
Overview
Injective is a DeFi focused layer-1 blockchain built for the next generation of decentralized derivatives exchanges. The Injective Chain is a Tendermint-based IBC-compatible blockchain which supports a decentralized orderbook-based DEX protocol and a trustless ERC-20 token bridge to the Ethereum blockchain.
It is the first decentralized exchange focused layer-1 blockchain for perpetual swaps, futures, and spot trading that unlocks the full potential of decentralized derivatives and borderless DeFi. Every component of the protocol has been built to be fully trustless, censorship-resistant, publicly verifiable, and front-running resistant.
By providing the unrestricted and unprecedented ability to express diverse views in decentralized financial markets, we strive to empower individuals with the ability to more efficiently allocate capital in our society.
Architecture Overview
Injective enables traders to create and trade on arbitrary spot and derivative markets. The entire process includes on-chain limit orderbook management, on-chain trade execution, on-chain order matching, on-chain transaction settlement, and on-chain trading incentive distribution through the logic codified by the Injective Chain's exchange module.
Architecturally there are two main services that traders should concern themselves with:
- The Injective Chain node (the Chain API)
- The Injective Exchange API
The trading lifecycle is as follows:
- First, traders cryptographically sign a transaction containing one or more order messages (e.g.
MsgBatchCreateDerivativeLimitOrders
,MsgCreateSpotMarketOrder
,MsgCancelDerivativeLimitOrder
, etc. ). - Then the transaction is broadcasted to an Injective Chain node.
- The transaction is then added to the mempool and becomes included in a block. More details on this process can be found here.
- The handler for each respective message is run. During handler execution, order cancel and liquidation messages are processed immediately, whereas order creation messages are added to a queue.
- At the end of the block, the batch auction process for order matching begins.
- First, the queued market orders are executed against the resting orderbook (which does NOT include the new orders from the current block) and are cleared at a uniform clearing price.
- Second, the queued limit orders are matched against each other and the resting orderbook to result in an uncrossed orderbook. Limit orders created in that block are cleared at a uniform clearing price while resting limit orders created in previous blocks are cleared at an equal or better price than their limit order price.
- The funds are settled accordingly, with positions being created for derivative trades and assets being swapped for spot trades.
- Events containing the trade and settlement information are emitted by the Chain.
- The Injective Exchange API backend indexes the events and pushes updates to all subscribed traders.
Key Differences To CEX
- All information is public which includes things like untriggered Stop/Take orders or pending orders in the mempool.
- The data stored on-chain is minimal for performance reasons and reflects only the current state; exchange dApps provide additional historical data as well as a user interface for traders through the Injective Exchange API backend.
- Usually a DEX has front-running issues, but those are mitigated at Injective through fast block times and FBA (Frequent Batch Auction).
- The order of execution is different. Any new exchange action is a new transaction and is not executed immediately. Instead, it is added to a queue (mempool) and executed once the block is committed. At the time of the block commit, all included transactions happen more or less instantly. Firstly, code that is inside the handler is executed in the transaction sequence which is decided by the miner. This is not a problem since the sequence does not affect matching prices due to FBA and thus fairness is guaranteed.
To summarize the sequence of state changes on the Injective Chain:
- Mempool: A queue of pending transactions.
- BeginBlocker: Code that is executed at the beginning of every block. We use it for certain maintenance tasks (details can be found in the exchange module documentation).
- Handler: Code that is executed when a transaction is included in a block.
- EndBlocker: Code that is executed at the end of every block. We use it to match orders, calculate changes in funds, and update positions.
Comparison to CEX
Centralized Exchange (CEX) | Decentralized Exchange (DEX) |
---|---|
Exchange Gateway | Injective Chain Handler |
Exchange Matching Engine | Injective Chain EndBlocker |
Exchange Trade Report | Injective Chain EndBlocker |
Co-location | Injective Node (Decentralized Validators) |
Frequent Batch Auction (FBA)
The goal is to further prevent any Front-Running in a decentralized setting. Most DEX's suffer from this as all information is public and traders can collude with miners or pay high gas fees enabling them to front-run any trades. We mitigate this by combining fast block times with a Frequent Batch Auction:
In any given block:
- Calculate one uniform clearing price for all market orders and execute them. For an example for the market order matching in FBA fashion, look here.
- Limit orders are combined with the resting orderbook and orders are matched as long as there is still negative spread. The limit orders are all matched at one uniform clearing price. For an example for the limit order matching in FBA fashion, look here.
Trading Fees and Gas
If you are a trader on existing centralized exchanges, you will be familiar with the concept of trading fees. Traders are charged a fee for each successful trade. However, for a DEX, there are additional gas costs that must be paid to the network. And luckily, the gas fee from trading on Injective is very minimal.
- If you are a trader using a DEX UI, you don't need to worry about gas costs because the exchange dApp will pay them for you. However, you will pay trading fees in full.
- If you are using the API, then you will need to pay the gas costs.
- The gas costs are currently minimal, 20K transactions will cost about 1 INJ.
- You can set the fee_recipient to your own wallet address to save 40% of all trading fees.
Note: trading from bank balances, which automatically uses the default subaccount 0, will cost roughly 15% more gas than trading from other subaccounts. API traders can use other subaccounts to trade to avoid the extra gas fees—read here for more information.
Mark Price Margin Requirement
Quantity = 2 BTC, InitialMarginRatio = 0.05
MarkPrice = $45,000, EntryPrice = $43,000
Margin ≥ 2 * 0.05 * $45,000 = $4,500
MarginLong ≥ max(2 * (0.05 * $45,000 - ($45,000 - $43,000)), $4,500)
MarginLong ≥ max($500, $4,500) = $4,500
MarginShort ≥ max(2 * (0.05 * $45,000 - ($43,000 - $45,000)), $4,500)
MarginShort ≥ max($8,500, $4,500) = $8,500
So in this case if the trader wanted to create a short position with
an entry price which essentially starts at a loss of $2,000 as
unrealized PNL, he would need to post at a minimum $8,500 as margin,
rather than the usual required $4,500.
You might be familiar with margin requirements on Centralized Exchanges. When creating a new position, it must fulfill the following requirement:
Margin >= InitialMarginRatio * Quantity * EntryPrice
For example in a market with maximally 20x leverage, your initial margin must be at least 0.05 of the order's notional (entryPrice * quantity
). On Injective additionally the margin must also fulfill the following mark price requirement:
Margin >= Quantity * (InitialMarginRatio * MarkPrice - PNL)
where PNL
is the expected profit and loss of the position if it was closed at the MarkPrice.
Liquidations
Long Position:
Quantity = 1 BTC, MaintenanceMarginRatio = 0.05
EntryPrice = $50,000, Margin = $5,000
Now the MarkPrice drops down to $47,300, which is below the liquidation price of $47,368.42 (when margin = $2,368.42, maintenance ratio ≈ .04999998).
The position is auto-closed via reduce-only order:
Sell order:
Quantity = 1 BTC, Price = $0, Margin = $0
Assuming it gets matched with a clearing price of 47,100:
Liquidation Payout = Position Margin + PNL = $5,000 - $2,900 = $2,100
Liquidator Profit = $2,100 * 0.5 = $1,050
Insurance Fund Profit = $2,100 * 0.5 = $1,050
When your position falls below the maintenance margin ratio, the position can and likely will be liquidated by anyone running the liquidator bot. You will loose your entire position and all funds remaining in the position. On-chain, a reduce-only market order of the same size as the position is automatically created. The market order will have a worst price defined as Infinity or 0, implying it will be matched at whatever prices are available in the order book.
One key difference is that the payout from executing the reduce-only market order will not go towards the position owner. Instead, half of the remaining funds are transferred to the liquidator bot and the other half is transferred to the insurance fund.
If the payout in the position was negative, i.e., the position's negative PNL was greater than its margin, then the insurance fund will cover the missing funds.
Note: liquidations are executed immediately in a block before any other order matching occurs.
Fee Discounts
Fee discounts are enabled by looking at the past trailing 30 day window. As long as you meet both conditions for a tier (volume traded AND staked amount), you will receive the respective discounts.
- Note that there is a caching mechanism in place which can take up to one day before being updated with a new tier.
- Negative maker fee markets are not eligible for discounts.
Funding Rate
The hourly funding rate on perpetual markets determines the percentage that traders on one side have to pay to the other side each hour. If the rate is positive, longs pay shorts. If the rate is negative, shorts pay longs. The further trade prices deviate from the mark price within the hour, the higher the funding rate will be up to a maximum of 0.0625% (1.5% per day).
Closing a Position
Suppose you have an open position:
- Direction = Long
- Margin = $5,000
- EntryPrice = $50,000
- Quantity = 0.5 BTC
You create a new vanilla order for
- Direction = Sell
- Margin = $10,000
- Price = $35,000
- Quantity = 0.75 BTC
which is fully matched. First, the position is fully closed:
- OrderMarginUsedForClosing = OrderMargin * CloseQuantity / OrderQuantity
- OrderMarginUsedForClosing = $10,000 * 0.5 / 0.75 = $6,667
The proportional closing order margin is then used for the payout:
- Payout = PNL + PositionMargin + OrderMarginUsedForClosing
- Payout = ($35,000-$50,000) * 0.5 + $5,000 + $6,667 = $4,167
And a new position is opened in the opposite direction:
- Direction = Short
- Margin = $3,333
- Price = $35,000
- Quantity = 0.25 BTC
There are two ways to close a position:
Closing via Reduce-Only Order
When you close a position via a reduce-only order, no additional margin is used from the order. All reduce-only orders have a margin of zero. In addition, reduce-only orders are only used to close positions, not to open new ones.
Closing via Vanilla Order
You can also close a position via vanilla orders. When a sell vanilla order is getting matched while you have an open Long position, the position will be closed at the price of the sell order. Depending on the size of the order and position, the position may be either
- partially closed
- fully closed
- or fully closed with subsequent opening of a new position in the opposite direction.
Note that how the margin inside the order is used depends on which of the three scenarios you are in. If you close a position via vanilla order, the margin is only used to cover PNL payouts, not to go into the position. If the order subsequently opens a new position in the opposite direction (scenario 3), the remaining proportional margin will go towards the new position.
Trading Rewards
Assume you have a trading rewards campaign with 100 INJ as rewards:
Reward Tokens: 100 INJ
Trader Reward Points = 100
Total Reward Points = 1,000
Trader Rewards = Trader Reward Points / Total Reward Points * Reward Tokens
Trader Rewards = 100 / 1,000 * 100 INJ = 10 INJ
During a given campaign, the exchange will record each trader's cumulative trading reward points obtained from trading fees (with boosts applied, if applicable) from all eligible markets. At the end of each campaign each trader will receive a pro-rata percentage of the trading rewards pool based off their trading rewards points from that campaign epoch. Those rewards will be automatically deposited into the trader's respective wallets, it's not necessary to manually withdraw them.
Reduce-Only Order Precedence
Imagine a trader has the following position:
- LONG:
1 BTC
with EntryPrice of $59,000
And the following SELL orders:
Buy Price | Quantity | Order Type |
---|---|---|
$66,500 | 0.2 BTC | Vanilla |
$65,500 | 0.1 BTC | Reduce-only |
$65,400 | 0.1 BTC | Vanilla |
$64,500 | 0.3 BTC | Vanilla |
$63,500 | 0.1 BTC | Reduce-only |
This has some implications when placing new orders.
Upon placing a reduce-only order:
We check if any reduce-only orders would be invalid after executing all of the trader's other limit sell orders that have better prices in the same direction.
In our example, consider a new reduce-only order of 0.4 BTC
at $64,600
.
Sell Price | Quantity | Order Type |
---|---|---|
$66,500 | 0.2 BTC | Vanilla |
$65,500 | 0.1 BTC | Reduce-only |
$65,400 | 0.1 BTC | Vanilla |
$64,600 | 0.4 BTC | Reduce-only |
$64,500 | 0.3 BTC | Vanilla |
$63,500 | 0.1 BTC | Reduce-only |
This is perfectly valid and no further action is required. If the buy price hit $65,500 and all limit sell orders less than or equal to that price were filled, then the long position would be closed. If the price hit $66,500 and the vanilla sell order was filled, then the trader would open a 0.2 BTC short position. But what if the reduce-only order was for 0.5 BTC
 instead?
Sell Price | Quantity | Order Type |
---|---|---|
$66,500 | 0.2 BTC | Vanilla |
$65,500 | 0.1 BTC | Reduce-only |
$65,400 | 0.1 BTC | Vanilla |
$64,600 | 0.5 BTC | Reduce-only |
$64,500 | 0.3 BTC | Vanilla |
$63,500 | 0.1 BTC | Reduce-only |
If the orders are getting matched, once the last vanilla order of 0.1 BTC at $65,400 is filled, the position will have been reduced to 1 BTC - 0.1 BTC - 0.3 BTC - 0.5 BTC - 0.1 BTC = 0 BTC
. The next reduce-only order of 0.1 BTC at $65,500 will thus be invalid.
To prevent that, we automatically cancel all reduce-only orders at a price where the cumulative sum of orders up to and including the reduce-only order would add up to more than the trader’s current long amount. Another way to think about it: we find the reduce-only order with the highest price such that all orders (vanilla and reduce-only) including and below that price add up in quantity to less than the long quantity. All reduce-only orders above that price will be canceled so that no reduce-only orders exist when the position is closed or short. The same concept applies to reduce-only orders on short positions, but we look for the lowest price instead of the highest on buy orders so that no reduce-only orders exist when the position is closed or long.
Upon placing a vanilla limit order:
We check if any reduce-only limit orders would be invalidated if all the orders up to and including the new vanilla limit order were filled.
In our example, consider a new vanilla order of 0.4 BTC
at $64,600
.
Sell Price | Quantity | Order Type |
---|---|---|
$66,500 | 0.2 BTC | Vanilla |
$65,500 | 0.1 BTC | Reduce-only |
$65,400 | 0.1 BTC | Vanilla |
$64,600 | 0.4 BTC | Vanilla |
$64,500 | 0.3 BTC | Vanilla |
$63,500 | 0.1 BTC | Reduce-only |
Again this perfectly valid and no further action is required because all order quantities up to the highest priced reduce-only order add up to ≤ the long position quantity. But what if the order was for 0.5 BTC
instead?
Sell Price | Quantity | Order Type |
---|---|---|
$66,500 | 0.2 BTC | Vanilla |
$65,500 | 0.1 BTC | Reduce-only |
$65,400 | 0.1 BTC | Vanilla |
$64,600 | 0.5 BTC | Vanilla |
$64,500 | 0.3 BTC | Vanilla |
$63,500 | 0.1 BTC | Reduce-only |
If the orders are getting matched, once the last reduce-only order of $65,500 is reached, the position will have been reduced to 1 BTC - 0.1 BTC - 0.3 BTC - 0.5 BTC - 0.1 BTC = 0 BTC
. A reduce-only order of 0.1 BTC after that will thus be invalid.
To prevent this, we automatically cancel the existing 0.1 BTC reduce-only order. In other words, new vanilla limit orders can invalidate and auto-cancel existing reduce-only limit orders if the reduce-only order becomes invalid at its price.
Rate Limits
The public mainnet and testnet nodes have a request rate limit associated to the requester IP address. The limits are:
- 20 requests/second for the "chain" group
- 50 requests/second for the "indexer" group
Each endpoint's section in this document clarifies which group the endpoint belongs to. When the limit is reached the server will respond sending an error response with code 429.
Order types
- BUY (1): A standard buy order to purchase an asset at either the current market price or a set limit price. Market orders in Injective also have a price to stablish a limit to the market price the order will be executed with.
- SELL (2): A standard sell order to sell an asset at either the current market price or a set limit price. Market orders in Injective also have a price to stablish a limit to the market price the order will be executed with.
- STOP_BUY (3): A stop-loss buy order converts into a regular buy order once the oracle price reaches or surpasses a specified trigger price.
- STOP_SELL (4): A stop-loss sell order becomes a regular sell order once the oracle price drops to or below a specified trigger price.
- TAKE_BUY (5): A take-profit buy order converts into a regular buy order once the oracle price reaches or surpasses a specified trigger price.
- TAKE_SELL (6): A take-profit sell order becomes a regular sell order once the oracle price drops to or below a specified trigger price.
- BUY_PO (7): Post-Only Buy. This order type ensures that the order will only be added to the order book and not match with a pre-existing order. It guarantees that you will be the market "maker" and not the "taker".
- SELL_PO (8): Post-Only Sell. Similar to BUY_PO, this ensures that your sell order will only add liquidity to the order book and not match with a pre-existing order.
- BUY_ATOMIC (9): An atomic buy order is a market order that gets executed instantly, bypassing the Frequent Batch Auctions (FBA). It's intended for smart contracts that need to execute a trade instantly. A higher fee is paid defined in the global exchange parameters (currently it is two times the normal trading fee).
- SELL_ATOMIC (10): An atomic sell order is similar to a BUY_ATOMIC, and it gets executed instantly at the current market price, bypassing the FBA.
Market and Limit Order Examples
Adding a Spot Market Buy Order
Maker Fee = -0.01%
Taker Fee = 0.1%
Market Buy Order:
Quantity = 1,000 INJ
Worst Price = 5 USDT
→ The account's available balance is decremented by 5,000 USDT + Taker Fee = 5,005 USDT
.
Upon matching with a resting sell order with price of 4 USDT
the new account balances are calculated as:
Trading Fee = 1,000 * 4 * 0.001 = 4 USDT
Credit Amount = 1,000 INJ
Debit Amount = 1,000 * 4 + 4 = 4,004 USDT
Clearing Refund = 5,005 - 4,004 = 1,001 USDT
Adding a Spot Market Sell Order
Maker Fee = -0.01%
Taker Fee = 0.1%
Market Sell Order:
Quantity = 1,000 INJ
Worst Price = 3 USDT
→ The account's available balance is decremented by 1,000 INJ
.
Upon matching with a resting sell order with price of 4 USDT
the new account balances are calculated as:
Trading Fee = 1,000 * 4 * 0.001 = 4 USDT
Debit Amount = 1,000 INJ
Credit Amount = 1,000 * 4 - 4 = 3,996 USDT
Clearing Refund = 0
Adding a Spot Limit Buy Order
Maker Fee = -0.01%
Taker Fee = 0.1%
We initially assume taker fees for the limit order in case the limit order is matched immediately. With price and quantity of:
Quantity = 1,000 INJ
Price = 5 USDT
→ The account's available balance is decremented by 5,000 USDT + Taker Fee = 5,005 USDT
.
After the order is submitted:
- If Matched Immediately: the limit order is filled and no fees are refunded since taker fees were previously deducted from the account's available balance. However, if Post-Only was selected, then the order will not be matched and will be rejected, with the trading fees being refunded. Assuming a clearing price of 4 USDT and a non Post-Only order:
Trading Fee = 1,000 * 4 * 0.001 = 4 USDT
Credit Amount = 5,000 - 4,000 = 1,000 INJ
Debit Amount = 1,000 * 4 + 4 = 4,004 USDT
Clearing Refund = 5,005 - 4,004 = 1,001 USDT
Unmatched Fee Refund = 0 USDT
- If Entirely Unmatched, the order becomes a resting limit order and we refund the taker fee:
Fee Refund = 1,000 * 5 * (0.001) = 5 USDT
- If Filled Later by Market Order, a maker fee will be charged, or a rebate will be credited. Since the order is filled by Market Order, the clearing price will be the price set in the limit order:
Trading Fee Rebate = 1,000 * 5 * -0.0001 = 0.5 USDT
Credit Amount = 1,000 INJ + 0.5 USDT
Debit Amount (in quote asset) = 1,000 * 5 = 5,000 USDT
If Partially Matched Immediately: the portion of the limit order that is filled immediately is charged taker fees with the rest being charged/credited maker fees/rebates. Assuming half the order is matched at a clearing price of 4 USDT and half the order is filled by Market Order at 5 USDT:
- Portion Immediately Filled:
Taker Trading Fee = 500 * 4 * 0.001 = 2 USDT
Credit Amount = 500 INJ
Debit Amount (Including Fees) = 500 * 4 + 2 = 2,002 USDT
Clearing Refund = (quantity of order filled * limit price) - (quantity of order filled * clearing price) + (proportional difference between limit fees and clearing fees)= (500 * 5) - (500 * 4) + ((500 * 5) * 0.001 - (500 * 4) * 0.001) = 500.5 USDT
Unmatched Fee Refund = quantity of order unfilled * limit price * taker fee rate = 500 * 5 * 0.001 = 2.5 USDT
- Rest of Order Filled Later by Market Order:
Maker Trading Fee Rebate = 500 * 5 * -0.0001 = 0.25 USDT
Credit Amount = 500 INJ
Debit Amount = 500 * 5 = 2,500 USDT
Clearing Refund = 0 USDT
- In Total:
Net Trading Fee = 2 - 0.25 = 1.75 USDT
Credit Amount (Including Maker Fee Rebates) = 1000 INJ + 0.25 USDT
Debit Amount (Including Taker Fees) = 4,502 USDT
- Portion Immediately Filled:
Adding a Spot Limit Sell Order
Maker Fee = -0.01%
Taker Fee = 0.1%
We initially assume taker fees for the limit order in case the limit order is matched immediately. With price and quantity of:
Quantity = 1,000 INJ
Price = 3 USDT
→ The account's available balance is decremented by 1,000 INJ
.
After the order is submitted:
- If Matched Immediately: the limit order is filled and taker fees are deducted, unless Post-Only is selected, in which case the order will not be matched and will be rejected with no trading fees being charged. Assuming a clearing price of 4 USDT:
Trading Fee = 1,000 * 4 * 0.001 = 4 USDT
Credit Amount = 1,000 * 4 - 4 = 3,996 USDT
Debit Amount = 1,000 INJ
Clearing Refund = 0 ETH
Fee Refund/Rebate = 0 USDT
- If Filled Later by Market Order, a maker fee rebate will be credited. Since the order is filled by Market Order, the clearing price will be the price set in the limit order:
Maker Trading Rebate = 1,000 * 3 * 0.0001 = 0.3 USDT
Credit Amount (in quote asset) = 1,000 * 3 + 0.3 = 3,000.3 USDT
Debit Amount (in base asset) = 1,000 INJ
- If Partially Matched Immediately: the portion of the limit order that is filled immediately is charged taker fees with the rest being charged maker fees. Similar logic to a spot limit buy order applies.
Derivative Market Order Payouts
The payouts for derivative market orders work the same way as for derivative limit orders, with the one difference being they are cancelled if not immediately matched. See spot market and derivative limit orders as reference.
Adding a Derivative Limit Buy Order
Quantity = 1,000 INJ
,Price = 5 USDT
,Margin = 1,000 USDT
TakerFeeRate = 0.001
MakerFeeRate = -0.0001
→ The account's available balance is decremented by Margin + Taker Fee = 1000 + 5000 * 0.001 = 1005 USDT
.
After creation:
If Unmatched, the order becomes a resting limit order (maker) and we refund the taker fee on vanilla orders (reduce-only orders don't pay upfront fees):
Fee Refund = 5 USDT
If Matched:
Assuming:
- a clearing price of 4 USDT
an existing
SHORT
position:Position Quantity = 600 INJ
Position Entry Price = 4.5 USDT
Position Margin = 400 USDT
Would result in:
1. Closing existing position with proportional order margin for closing:
CloseExecutionMargin = ExecutionMargin * CloseQuantity / OrderQuantity = 1000 * 600 / 1000 = 600 USDT
- Where
CloseExecutionMargin = Portion of margin used to close position
- And
ExecutionMargin = Margin supplied in new order
- Where
ClosingPayout = PNL + PositionMargin * CloseQuantity / PositionQuantity + CloseExecutionMargin
Short PNL = CloseQuantity * (EntryPrice - FillPrice) = 600 * (4.5 - 4) = 300 USDT
ClosingPayout = 300 + 400 * 600 / 600 + 600 = 1300 USDT
2. Opening new position in opposite direction:
a new
LONG
position:Position Quantity = 400 INJ
Position Entry Price = 4 USDT
NewPositionMargin = ExecutionMargin - CloseExecutionMargin = 1000 - 600 = 400 USDT
3. Refunding margin difference from order price vs. clearing price:
- Since the order was placed with 5x leverage at a price of 5 USDT, some margin is refunded with the new clearing price to maintain the 5x leverage.
Margin Refund = NewPositionMargin - NewPositionMarginRequired = 400 - 400 * 4 / 5 = 80 USDT
4. Refunding fee difference from order price vs. clearing price:
PriceDelta = Price - ClearingPrice = 5 - 4 = 1 USDT
ClearingFeeRefund = FillQuantity * PriceDelta * TakerFeeRate = 1000 * 1 * 0.001 = 1 USDT
- In the case of matching a sell order, this would have been a charge, not a refund
Market Order Matching
Existing Orderbook
Sells | Buys | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
New Orders
- 1x market buy order for 0.2 BTC with worst price 64,360
- 1x market buy order for 0.4 BTC with worst price 66,000
- 1x market sell order for 0.1 BTC with worst price 60,000
- 1x market sell order for 0.2 BTC with worst price 61,000
- 1x market sell order for 0.3 BTC with worst price 69,000
Resulting Orderbook
Sells | Buys | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
Market Buys: Matching the highest priced market buy order first for 0.4 BTC. Now for the second market buy order only 0.1 BTC is left at matchable price, meaning the other 0.1 BTC in the order will be cancelled. Both orders will be matched with the single resting limit order at a price of 64,360 for a total quantity of 0.5 BTC.
Market Sells: Matching the first two market sell orders for at a matching price of (64,210*0.1 + 64,205*0.2) / 0.3 = 64,206.67
for a total quantity of 0.3 BTC. The resting limit orders are both matched at their specified price points of 64,210 and 64,205. Since the last market sell order of 69,000 cannot be fulfilled, it is cancelled.
Limit Order Matching
Existing Orderbook
Sells | Buys | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
New Orders
Sells | Buys | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
Matching Orders
All new orders are incorporated into the existing orderbook. In our case this results in a negative spread:
Sells | Buys | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
As long as negative spread exists, orders are matched against each other. The first buy order is fully matched:
Sells | Buys | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
Now the second buy order can still be fully matched:
Sells | Buys | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
This is the end of the matching, since no more negative spread exists (64,220 > 62,210
).
All orders will be matched with a uniform clearing price within the range of the last sell order price and the last buy order price.
- Last sell order price: 64,220
- Last buy order price: 64,360
- 64,220 >= Clearing price >= 64,360
Step 1: Check if clearing price range is out of bounds regarding the resting orderbook mid price.
- Resting orderbook mid price: (64,250+64,210)/2 = 64,230
- Is within range of clearing price ✅ (if not, a clearing price of either last buy or last sell price would be used)
Step 2: Check if clearing price range is out of bounds regarding the mark price.
- Let's assume mark price is 64,300
- Is within range of clearing price ✅ (if not, a clearing price of either last buy or last sell price would be used)
Step 3: Set clearing price = mid price or mark price for spot or perpetual markets, respectively, or in the case where these prices are out of bounds, use last buy or last sell price.
Resources
Here you can find a comprehensive overview of the exchange ecosystem on Injective, guides and general resources for developers and API traders.
Coin denoms and market IDs for testnet and mainnet can be found on the Injective testnet explorer and mainnet explorer under the Markets tab and Assets tab.
Explorer
A Web interface that allows you to search for information on the Injective Chain
Faucet
A web-based service that provides free tokens to users on testnet and allows them to experiment on the Injective Chain.
Status
Monitor the uptime of all public services.
Message Broadcaster
In the examples included in this documentation you will see all the steps required to interact with the chain, from deriving a public key from a private key, creating messages to query the chain or creating orders, to creating and broadcasting transactions to the chain. Before going to the examples of all the possible actions it is important to state that you can avoid implementing yourself all the steps to create and configure correctly a transaction. If you are not interested in defining all the low level aspects you can use the component called MsgBroadcasterWithPk. To use the broadcaster you just need to create an instance of MsgBroadcasterWithPk, and once all the messages to be included in the transaction have been created, use the broadcast method, passing the messages as a parameter. The broadcaster will take care of: - Calculate the gas fee to pay for the transaction - Create the transaction and configure it - Sign the transaction - Broadcast it to the chain
Broadcaster for standard account
Calculate gas fee simulating the transaction
Example - Calculate gas fee simulating the transaction:
import asyncio
import os
import uuid
from decimal import Decimal
import dotenv
from pyinjective.composer import Composer as ProtoMsgComposer
from pyinjective.core.broadcaster import MsgBroadcasterWithPk
from pyinjective.core.network import Network
from pyinjective.wallet import PrivateKey
async def main() -> None:
dotenv.load_dotenv()
private_key_in_hexa = os.getenv("INJECTIVE_PRIVATE_KEY")
# select network: local, testnet, mainnet
network = Network.testnet()
composer = ProtoMsgComposer(network=network.string())
message_broadcaster = MsgBroadcasterWithPk.new_using_simulation(
network=network,
private_key=private_key_in_hexa,
)
priv_key = PrivateKey.from_hex(private_key_in_hexa)
pub_key = priv_key.to_public_key()
address = pub_key.to_address()
subaccount_id = address.get_subaccount_id(index=0)
# prepare trade info
fee_recipient = "inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r"
spot_market_id_create = "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe"
spot_orders_to_create = [
composer.spot_order(
market_id=spot_market_id_create,
subaccount_id=subaccount_id,
fee_recipient=fee_recipient,
price=Decimal("3"),
quantity=Decimal("55"),
order_type="BUY",
cid=(str(uuid.uuid4())),
),
composer.spot_order(
market_id=spot_market_id_create,
subaccount_id=subaccount_id,
fee_recipient=fee_recipient,
price=Decimal("300"),
quantity=Decimal("55"),
order_type="SELL",
cid=str(uuid.uuid4()),
),
]
# prepare tx msg
msg = composer.msg_batch_update_orders(
sender=address.to_acc_bech32(),
spot_orders_to_create=spot_orders_to_create,
)
# broadcast the transaction
result = await message_broadcaster.broadcast([msg])
print("---Transaction Response---")
print(result)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
For the broadcaster to calculate the gas fee running the simulation, create an instance of MsgBroadcasterWithPk
with the message new_using_simulation
.
This is the most common broadcaster configuration. Unless you are using grantee accounts (delegated accounts with authz) you should use this one.
Calculate gas fee without simulation
Example - Calculate gas fee without simulation:
import asyncio
import os
import uuid
from decimal import Decimal
import dotenv
from pyinjective.composer import Composer as ProtoMsgComposer
from pyinjective.core.broadcaster import MsgBroadcasterWithPk
from pyinjective.core.network import Network
from pyinjective.wallet import PrivateKey
async def main() -> None:
dotenv.load_dotenv()
private_key_in_hexa = os.getenv("INJECTIVE_PRIVATE_KEY")
# select network: local, testnet, mainnet
network = Network.testnet()
composer = ProtoMsgComposer(network=network.string())
message_broadcaster = MsgBroadcasterWithPk.new_without_simulation(
network=network,
private_key=private_key_in_hexa,
)
priv_key = PrivateKey.from_hex(private_key_in_hexa)
pub_key = priv_key.to_public_key()
address = pub_key.to_address()
subaccount_id = address.get_subaccount_id(index=0)
# prepare trade info
fee_recipient = "inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r"
spot_market_id_create = "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe"
spot_orders_to_create = [
composer.spot_order(
market_id=spot_market_id_create,
subaccount_id=subaccount_id,
fee_recipient=fee_recipient,
price=Decimal("3"),
quantity=Decimal("55"),
order_type="BUY",
cid=str(uuid.uuid4()),
),
composer.spot_order(
market_id=spot_market_id_create,
subaccount_id=subaccount_id,
fee_recipient=fee_recipient,
price=Decimal("300"),
quantity=Decimal("55"),
order_type="SELL",
cid=str(uuid.uuid4()),
),
]
# prepare tx msg
msg = composer.msg_batch_update_orders(
sender=address.to_acc_bech32(),
spot_orders_to_create=spot_orders_to_create,
)
# broadcast the transaction
result = await message_broadcaster.broadcast([msg])
print("---Transaction Response---")
print(result)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
For the broadcaster to calculate the gas fee based on the messages included without running the simulation, create an instance of MsgBroadcasterWithPk
with the message new_without_simulation
.
Broadcaster for grantee account
This is the required broadcaster configuration when operating with grantee accounts. The broadcaster will take care of creating the MsgExec
message, so that the user keeps passing the same messages to the broadcast
method that are passed when using the standard broadcaster with non-grantee accounts.
Calculate gas fee simulating the transaction
Example - Calculate gas fee simulating the transaction:
import asyncio
import os
import uuid
from decimal import Decimal
import dotenv
from pyinjective.async_client import AsyncClient
from pyinjective.composer import Composer as ProtoMsgComposer
from pyinjective.core.broadcaster import MsgBroadcasterWithPk
from pyinjective.core.network import Network
from pyinjective.wallet import Address, PrivateKey
async def main() -> None:
dotenv.load_dotenv()
private_key_in_hexa = os.getenv("INJECTIVE_GRANTEE_PRIVATE_KEY")
granter_inj_address = os.getenv("INJECTIVE_GRANTER_PUBLIC_ADDRESS")
# select network: local, testnet, mainnet
network = Network.testnet()
composer = ProtoMsgComposer(network=network.string())
# initialize grpc client
client = AsyncClient(network)
await client.sync_timeout_height()
# load account
priv_key = PrivateKey.from_hex(private_key_in_hexa)
pub_key = priv_key.to_public_key()
address = pub_key.to_address()
message_broadcaster = MsgBroadcasterWithPk.new_for_grantee_account_using_simulation(
network=network,
grantee_private_key=private_key_in_hexa,
)
# prepare tx msg
market_id = "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe"
granter_address = Address.from_acc_bech32(granter_inj_address)
granter_subaccount_id = granter_address.get_subaccount_id(index=0)
msg = composer.msg_create_spot_limit_order(
market_id=market_id,
sender=granter_inj_address,
subaccount_id=granter_subaccount_id,
fee_recipient=address.to_acc_bech32(),
price=Decimal("7.523"),
quantity=Decimal("0.01"),
order_type="BUY",
cid=str(uuid.uuid4()),
)
# broadcast the transaction
result = await message_broadcaster.broadcast([msg])
print("---Transaction Response---")
print(result)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
For the broadcaster to calculate the gas fee running the simulation, create an instance of MsgBroadcasterWithPk
with the message new_for_grantee_account_using_simulation
.
Calculate gas fee without simulation
Example - Calculate gas fee without simulation:
import asyncio
import os
import uuid
from decimal import Decimal
import dotenv
from pyinjective.async_client import AsyncClient
from pyinjective.composer import Composer as ProtoMsgComposer
from pyinjective.core.broadcaster import MsgBroadcasterWithPk
from pyinjective.core.network import Network
from pyinjective.wallet import Address, PrivateKey
async def main() -> None:
dotenv.load_dotenv()
private_key_in_hexa = os.getenv("INJECTIVE_GRANTEE_PRIVATE_KEY")
granter_inj_address = os.getenv("INJECTIVE_GRANTER_PUBLIC_ADDRESS")
# select network: local, testnet, mainnet
network = Network.testnet()
composer = ProtoMsgComposer(network=network.string())
# initialize grpc client
client = AsyncClient(network)
await client.sync_timeout_height()
# load account
priv_key = PrivateKey.from_hex(private_key_in_hexa)
pub_key = priv_key.to_public_key()
address = pub_key.to_address()
message_broadcaster = MsgBroadcasterWithPk.new_for_grantee_account_without_simulation(
network=network,
grantee_private_key=private_key_in_hexa,
)
# prepare tx msg
market_id = "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe"
granter_address = Address.from_acc_bech32(granter_inj_address)
granter_subaccount_id = granter_address.get_subaccount_id(index=0)
msg = composer.msg_create_spot_limit_order(
market_id=market_id,
sender=granter_inj_address,
subaccount_id=granter_subaccount_id,
fee_recipient=address.to_acc_bech32(),
price=Decimal("7.523"),
quantity=Decimal("0.01"),
order_type="BUY",
cid=str(uuid.uuid4()),
)
# broadcast the transaction
result = await message_broadcaster.broadcast([msg])
print("---Transaction Response---")
print(result)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
For the broadcaster to calculate the gas fee based on the messages included without running the simulation, create an instance of MsgBroadcasterWithPk
with the message new_for_grantee_account_without_simulation
.
NOTE:
There an important consideration when using the Transaction Broadcaster calculating the gas cost without simulation to send a MsgBatchUpdateOrders message.
The logic that estimates the gas cost for the MsgBatchUpdateOrders correclty calculates the gas required for each order action (creation or cancelation) it includes. But there is no easy way to calculate the gas cost when canceling all orders for a market id using one of the following parameters: spot_market_ids_to_cancel_all
, derivative_market_ids_to_cancel_all
or binary_options_market_ids_to_cancel_all
. The complexity is related to the fact that the gas cost depends on the number of orders to be cancelled.
By default the estimation logic calculates a gas cost considering the number of orders to cancel for each market id is 20.
To improve the gas cost calculation when using the MsgBatchUpdateOrders message to cancel all orders for one or more markets you can change the number of estimated orders to cancel per market running the following command:
BatchUpdateOrdersGasLimitEstimator.AVERAGE_CANCEL_ALL_AFFECTED_ORDERS = 30
Fine-tunning gas fee local estimation
As mentioned before the gas estimation without using simulation is implemented by using fixed values as gas cost for certain messages and actions. Since the real gas cost can differ at some point from the estimator calculations, the Python SDK allows the developer to fine-tune certain gas cost values in order to improve the gas cost estimation. In the next tables you can find the global values used for gas estimation calculations, that can be modified in your application:
Module | Global Variable | Description |
---|---|---|
pyinjective.core.gas_limit_estimator |
SPOT_ORDER_CREATION_GAS_LIMIT |
The gas cost associated to the creation of one spot order |
pyinjective.core.gas_limit_estimator |
DERIVATIVE_ORDER_CREATION_GAS_LIMIT |
The gas cost associated to the creation of one derivative order |
pyinjective.core.gas_limit_estimator |
SPOT_ORDER_CANCELATION_GAS_LIMIT |
The gas cost associated to the cancellation of one spot order |
pyinjective.core.gas_limit_estimator |
DERIVATIVE_ORDER_CANCELATION_GAS_LIMIT |
The gas cost associated to the cancellation of one derivative order |
pyinjective.core.gas_limit_estimator |
SPOT_POST_ONLY_ORDER_MULTIPLIER |
Multiplier to increase the gas cost for post only spot orders (in addition to the normal spot order cost) |
pyinjective.core.gas_limit_estimator |
DERIVATIVE_POST_ONLY_ORDER_MULTIPLIER |
Multiplier to increase the gas cost for post only derivative orders (in addition to the normal derivative order cost) |
Class | Global Variable | Description |
---|---|---|
MessageBasedTransactionFeeCalculator |
TRANSACTION_GAS_LIMIT |
The gas cost associated to the TX processing |
GasLimitEstimator |
GENERAL_MESSAGE_GAS_LIMIT |
Generic base gas cost for any message |
GasLimitEstimator |
BASIC_REFERENCE_GAS_LIMIT |
Base gas cost for messages not related to orders. Each type of message will calculate its cost multiplying this reference cost by a multiplier |
DefaultGasLimitEstimator |
DEFAULT_GAS_LIMIT |
The gas cost for all messages for which there is no especial estimator implemented |
BatchUpdateOrdersGasLimitEstimator |
CANCEL_ALL_SPOT_MARKET_GAS_LIMIT |
This is an estimation of the gas cost per spot order cancel when cancelling all orders for a spot market |
BatchUpdateOrdersGasLimitEstimator |
CANCEL_ALL_DERIVATIVE_MARKET_GAS_LIMIT |
This is an estimation of the gas cost per derivative order cancel when cancelling all orders for a derivative market |
BatchUpdateOrdersGasLimitEstimator |
MESSAGE_GAS_LIMIT |
Estimation of the general gas amount required for a MsgBatchUpdateOrders (not for particular actions, but for the general message processing) |
BatchUpdateOrdersGasLimitEstimator |
AVERAGE_CANCEL_ALL_AFFECTED_ORDERS |
This global represents the expected number of orders to be cancelled when executing a "cancel all orders for a market" action |
ExecGasLimitEstimator |
DEFAULT_GAS_LIMIT |
Estimation of the general gas amount required for a MsgExec (for the general message processing) |
GenericExchangeGasLimitEstimator |
BASIC_REFERENCE_GAS_LIMIT |
Base gas cost for messages not related to orders. Each type of message will calculate its cost multiplying this reference cost by a multiplier |
Indexer API
The Indexer API is read-only whereas the Chain API is write and also includes a limited set of API requests to read data. The Chain API reads query the blockchain state from the node directly as opposed to the Indexer API which reconstructs state from events emitted by chain.
On a high-level the end-user trading applications and Injective Products use the Indexer API to read data and the Chain API to write data to the blockchain. Even though it’s possible to develop trading applications using the Chain API only, the Indexer API includes more methods, streaming support, gRPC, and also allows you to fetch historical data (the Chain API queries the blockchain state which doesn’t include historical records).
- InjectiveAccountsRPC
InjectiveAccountsRPC defines the gRPC API of the Exchange Accounts provider.
SubaccountsList
Get a list of subaccounts for a specific address.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def main() -> None:
network = Network.testnet()
client = AsyncClient(network)
account_address = "inj1clw20s2uxeyxtam6f7m84vgae92s9eh7vygagt"
subacc_list = await client.fetch_subaccounts_list(account_address)
print(subacc_list)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
)
func main() {
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
accountAddress := "inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku"
res, err := exchangeClient.GetSubaccountsList(ctx, accountAddress)
if err != nil {
fmt.Println(err)
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Parameter | Type | Description | Required |
---|---|---|---|
account_address | String | Injective address of the account to query for subaccounts | Yes |
Response Parameters
Response Example:
{
"subaccounts":[
"0xc7dca7c15c364865f77a4fb67ab11dc95502e6fe000000000000000000000001",
"0xc7dca7c15c364865f77a4fb67ab11dc95502e6fe000000000000000000000002",
"0xc7dca7c15c364865f77a4fb67ab11dc95502e6fe000000000000000000000000"
]
}
{
"subaccounts": [
"0xc7dca7c15c364865f77a4fb67ab11dc95502e6fe000000000000000000000001",
"0xc7dca7c15c364865f77a4fb67ab11dc95502e6fe000000000000000000000002"
]
}
Parameter | Type | Description |
---|---|---|
subaccounts | String Array | Subaccounts list |
SubaccountHistory
Get the subaccount's transfer history.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.client.model.pagination import PaginationOption
from pyinjective.core.network import Network
async def main() -> None:
network = Network.testnet()
client = AsyncClient(network)
subaccount = "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000"
denom = "inj"
transfer_types = ["withdraw", "deposit"]
skip = 1
limit = 15
end_time = 1665118340224
pagination = PaginationOption(skip=skip, limit=limit, end_time=end_time)
subacc_history = await client.fetch_subaccount_history(
subaccount_id=subaccount,
denom=denom,
transfer_types=transfer_types,
pagination=pagination,
)
print(subacc_history)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
accountPB "github.com/InjectiveLabs/sdk-go/exchange/accounts_rpc/pb"
)
func main() {
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
denom := "inj"
subaccountId := "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000"
transferTypes := []string{"deposit"}
skip := uint64(0)
limit := int32(10)
req := accountPB.SubaccountHistoryRequest{
Denom: denom,
SubaccountId: subaccountId,
TransferTypes: transferTypes,
Skip: skip,
Limit: limit,
}
res, err := exchangeClient.GetSubaccountHistory(ctx, &req)
if err != nil {
fmt.Println(err)
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Parameter | Type | Description | Required |
---|---|---|---|
subaccount_id | String | ID of the subaccount to get the history from | Yes |
denom | String | Filter by token denom | No |
transfer_types | String Array | Filter by transfer types. Valid options: internal, external, withdraw, deposit | No |
skip | Integer | Skip the first N items from the result | No |
limit | Integer | Maximum number of items to be returned | No |
end_time | Integer | Upper bound (inclusive) of account transfer history executed_at unix timestamp | No |
Response Parameters
Response Example:
{
"transfers":[
{
"transferType":"deposit",
"srcAccountAddress":"inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku",
"dstSubaccountId":"0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"amount":{
"denom":"inj",
"amount":"2000000000000000000"
},
"executedAt":"1665117493543",
"srcSubaccountId":"",
"dstAccountAddress":""
},
{
"transferType":"deposit",
"srcAccountAddress":"inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku",
"dstSubaccountId":"0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"amount":{
"denom":"inj",
"amount":"15000000000000000000"
},
"executedAt":"1660313668990",
"srcSubaccountId":"",
"dstAccountAddress":""
}
],
"paging":{
"total":"3",
"from":0,
"to":0,
"countBySubaccount":"0",
"next":[
]
}
}
{
"transfers": [
{
"transfer_type": "deposit",
"src_account_address": "inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku",
"dst_subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"amount": {
"denom": "inj",
"amount": "50000000000000000000"
},
"executed_at": 1651492257605
},
{
"transfer_type": "deposit",
"src_account_address": "inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku",
"dst_subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"amount": {
"denom": "inj",
"amount": "1000000000000000000"
},
"executed_at": 1652453978939
}
],
"paging": [
{
"total": 3
}
]
}
Parameter | Type | Description |
---|---|---|
transfers | SubaccountBalanceTransfer Array | Transfers list |
paging | Paging | Pagination details |
SubaccountBalanceTransfer
Parameter | Type | Description |
---|---|---|
transfer_type | String | Type of subaccount balance transfer |
src_subaccount_id | String | Subaccount ID of the sending side |
src_account_address | String | Account address of the sending side |
dst_subaccount_id | String | Subaccount ID of the receiving side |
dst_account_address | String | Account address of the receiving side |
amount | CosmosCoin | Transfer amount |
executed_at | Integer | Transfer timestamp (in milliseconds) |
CosmosCoin
Parameter | Type | Description |
---|---|---|
denom | String | Token denom |
amount | String | Token amount |
Paging
Parameter | Type | Description |
---|---|---|
total | Integer | Total number of available records |
from | Integer | Record index start |
to | Integer | Record index end |
count_by_subaccount | Integer | Count entries by subaccount |
next | String Array | List of tokens to navigate to the next pages |
SubaccountBalance
Get the balance of a subaccount for a specific denom.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def main() -> None:
network = Network.testnet()
client = AsyncClient(network)
subaccount_id = "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000"
denom = "inj"
balance = await client.fetch_subaccount_balance(subaccount_id=subaccount_id, denom=denom)
print(balance)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
)
func main() {
network := common.LoadNetwork("mainnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
subaccountId := "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000"
denom := "inj"
res, err := exchangeClient.GetSubaccountBalance(ctx, subaccountId, denom)
if err != nil {
fmt.Println(err)
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Parameter | Type | Description | Required |
---|---|---|---|
subaccount_id | String | ID of the subaccount to get the balances from | Yes |
denom | String | Filter by token denom | Yes |
Response Parameters
Response Example:
{
"balance":{
"subaccountId":"0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"accountAddress":"inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku",
"denom":"inj",
"deposit":{
"totalBalance":"0",
"availableBalance":"0"
}
}
}
{
"balance": {
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"account_address": "inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku",
"denom": "inj",
"deposit": {
"total_balance": "1492235700000000000000",
"available_balance": "1492235700000000000000"
}
}
}
Parameter | Type | Description |
---|---|---|
balance | SubaccountBalance | Balance details |
SubaccountBalance
Parameter | Type | Description |
---|---|---|
subaccount_id | String | Subaccount ID |
account_address | String | Injective address of the account the subaccount belongs to |
denom | String | Token denom |
deposit | SubaccountDeposit | Deposit details |
SubaccountDeposit
Parameter | Type | Description |
---|---|---|
total_balance | String | Total balance |
available_balance | String | Available balance |
SubaccountBalancesList
List the subaccount's balances for all denoms.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def main() -> None:
network = Network.testnet()
client = AsyncClient(network)
subaccount = "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000"
denoms = ["inj", "peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5"]
subacc_balances_list = await client.fetch_subaccount_balances_list(subaccount_id=subaccount, denoms=denoms)
print(subacc_balances_list)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
)
func main() {
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
subaccountId := "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000"
res, err := exchangeClient.GetSubaccountBalancesList(ctx, subaccountId)
if err != nil {
fmt.Println(err)
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Parameter | Type | Description | Required |
---|---|---|---|
subaccount_id | String | ID of the subaccount to get the balances from | Yes |
denoms | String | Filter balances by denoms. If not set, the balances of all the denoms for the subaccount are provided | No |
Response Parameters
Response Example:
{
"balances":[
{
"subaccountId":"0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"accountAddress":"inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku",
"denom":"peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5",
"deposit":{
"totalBalance":"131721505.337958346262317217",
"availableBalance":"0.337958346262317217"
}
},
{
"subaccountId":"0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"accountAddress":"inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku",
"denom":"inj",
"deposit":{
"totalBalance":"0",
"availableBalance":"0"
}
}
]
}
{
"balances": [
{
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"account_address": "inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku",
"denom": "peggy0xdAC17F958D2ee523a2206206994597C13D831ec7",
"deposit": {
"total_balance": "200501904612800.13082016560359584",
"available_balance": "200358014975479.130820165603595295"
}
},
{
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"account_address": "inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku",
"denom": "inj",
"deposit": {
"total_balance": "53790000010000000003",
"available_balance": "52790000010000000003"
}
},
{
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"account_address": "inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku",
"denom": "ibc/C4CFF46FD6DE35CA4CF4CE031E643C8FDC9BA4B99AE598E9B0ED98FE3A2319F9",
"deposit": {
"total_balance": "1000000",
"available_balance": "1000000"
}
}
]
}
Parameter | Type | Description |
---|---|---|
balances | SubaccountBalance Array | List of subaccount balances |
SubaccountBalance
Parameter | Type | Description |
---|---|---|
subaccount_id | String | Subaccount ID |
account_address | String | Injective address of the account the subaccount belongs to |
denom | String | Token denom |
deposit | SubaccountDeposit | Deposit details |
SubaccountDeposit
Parameter | Type | Description |
---|---|---|
total_balance | String | Total balance |
available_balance | String | Available balance |
SubaccountOrderSummary
Get a summary of the subaccount's active/unfilled orders.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def main() -> None:
network = Network.testnet()
client = AsyncClient(network)
subaccount = "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000"
order_direction = "buy"
market_id = "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6"
subacc_order_summary = await client.fetch_subaccount_order_summary(
subaccount_id=subaccount, order_direction=order_direction, market_id=market_id
)
print(subacc_order_summary)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
accountPB "github.com/InjectiveLabs/sdk-go/exchange/accounts_rpc/pb"
)
func main() {
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketId := "0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0"
subaccountId := "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000"
orderDirection := "buy"
req := accountPB.SubaccountOrderSummaryRequest{
MarketId: marketId,
SubaccountId: subaccountId,
OrderDirection: orderDirection,
}
res, err := exchangeClient.GetSubaccountOrderSummary(ctx, &req)
if err != nil {
fmt.Println(err)
}
fmt.Println("spot orders:", res.SpotOrdersTotal)
fmt.Println("derivative orders:", res.DerivativeOrdersTotal)
}
Parameter | Type | Description | Required |
---|---|---|---|
subaccount_id | String | ID of the subaccount to get the summary from | Yes |
market_id | String | Limit the order summary to a specific market | No |
order_direction | String | Filter by the direction of the orders. Valid options: buy, sell | No |
Response Parameters
Response Example:
{
"derivativeOrdersTotal":"1",
"spotOrdersTotal":"0"
}
spot orders: 1
derivative orders: 7
Parameter | Type | Description |
---|---|---|
spot_orders_total | Integer | Total count of subaccount's spot orders in given market and direction |
derivative_orders_total | Integer | Total count of subaccount's derivative orders in given market and direction |
StreamSubaccountBalance
Stream the subaccount's balance for all denoms.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from typing import Any, Dict
from grpc import RpcError
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def balance_event_processor(event: Dict[str, Any]):
print(event)
def stream_error_processor(exception: RpcError):
print(f"There was an error listening to balance updates ({exception})")
def stream_closed_processor():
print("The balance updates stream has been closed")
async def main() -> None:
network = Network.testnet()
client = AsyncClient(network)
subaccount_id = "0xc7dca7c15c364865f77a4fb67ab11dc95502e6fe000000000000000000000001"
denoms = ["inj", "peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5"]
task = asyncio.get_event_loop().create_task(
client.listen_subaccount_balance_updates(
subaccount_id=subaccount_id,
callback=balance_event_processor,
on_end_callback=stream_closed_processor,
on_status_callback=stream_error_processor,
denoms=denoms,
)
)
await asyncio.sleep(delay=60)
task.cancel()
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
)
func main() {
// network := common.LoadNetwork("mainnet", "k8s")
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
subaccountId := "0x1b99514e320ae0087be7f87b1e3057853c43b799000000000000000000000000"
stream, err := exchangeClient.StreamSubaccountBalance(ctx, subaccountId)
if err != nil {
panic(err)
}
for {
select {
case <-ctx.Done():
return
default:
res, err := stream.Recv()
if err != nil {
fmt.Println(err)
return
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
}
}
Parameter | Type | Description | Required |
---|---|---|---|
subaccount_id | String | ID of the subaccount to get the balances from | Yes |
denoms | String Array | Filter balances by denoms. If not set, the balances of all the denoms for the subaccount are provided | No |
Response Parameters
Streaming Response Example:
{
"balance": {
"subaccountId": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"accountAddress": "inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku",
"denom": "peggy0xdAC17F958D2ee523a2206206994597C13D831ec7",
"deposit": {
"totalBalance": "200493439765890.695319283887814576",
"availableBalance": "200493414240390.695319283887814031"
}
},
"timestamp": 1654234765000
}
{
"balance": {
"subaccountId": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"accountAddress": "inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku",
"denom": "peggy0xdAC17F958D2ee523a2206206994597C13D831ec7",
"deposit": {
"totalBalance": "200493847328858.695319283887814576",
"availableBalance": "200493821803358.695319283887814031"
}
},
"timestamp": 1654234804000
}
{
"balance": {
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"account_address": "inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku",
"denom": "peggy0xdAC17F958D2ee523a2206206994597C13D831ec7",
"deposit": {
"total_balance": "200503979400874.28368413692326264",
"available_balance": "200360046875708.283684136923262095"
}
},
"timestamp": 1653037703000
}{
"balance": {
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"account_address": "inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku",
"denom": "peggy0xdAC17F958D2ee523a2206206994597C13D831ec7",
"deposit": {
"total_balance": "200503560511302.28368413692326264",
"available_balance": "200359627986136.283684136923262095"
}
},
"timestamp": 1653037744000
}
Parameter | Type | Description |
---|---|---|
balance | SubaccountBalance | Subaccount balance |
timestamp | Integer | Operation timestamp in Unix milliseconds |
SubaccountBalance
Parameter | Type | Description |
---|---|---|
subaccount_id | String | Subaccount ID |
account_address | String | Injective address of the account the subaccount belongs to |
denom | String | Token denom |
deposit | SubaccountDeposit | Deposit details |
SubaccountDeposit
Parameter | Type | Description |
---|---|---|
total_balance | String | Total balance |
available_balance | String | Available balance |
OrderStates
Get orders with an order hash. This request will return market orders and limit orders in all states [booked, partial_filled, filled, canceled]. For filled and canceled orders, there is a TTL of 3 minutes. Should your order be filled or canceled you will still be able to fetch it for 3 minutes.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def main() -> None:
network = Network.testnet()
client = AsyncClient(network)
spot_order_hashes = [
"0xce0d9b701f77cd6ddfda5dd3a4fe7b2d53ba83e5d6c054fb2e9e886200b7b7bb",
"0x2e2245b5431638d76c6e0cc6268970418a1b1b7df60a8e94b8cf37eae6105542",
]
derivative_order_hashes = [
"0x82113f3998999bdc3892feaab2c4e53ba06c5fe887a2d5f9763397240f24da50",
"0xbb1f036001378cecb5fff1cc69303919985b5bf058c32f37d5aaf9b804c07a06",
]
orders = await client.fetch_order_states(
spot_order_hashes=spot_order_hashes, derivative_order_hashes=derivative_order_hashes
)
print(orders)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
accountPB "github.com/InjectiveLabs/sdk-go/exchange/accounts_rpc/pb"
)
func main() {
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
spotOrderHashes := []string{"0x0b156df549747187210ca5381f0291f179d76d613d0bae1a3c4fd2e3c0504b7c"}
derivativeOrderHashes := []string{"0x82113f3998999bdc3892feaab2c4e53ba06c5fe887a2d5f9763397240f24da50"}
req := accountPB.OrderStatesRequest{
SpotOrderHashes: spotOrderHashes,
DerivativeOrderHashes: derivativeOrderHashes,
}
res, err := exchangeClient.GetOrderStates(ctx, &req)
if err != nil {
fmt.Println(err)
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Parameter | Type | Description | Required |
---|---|---|---|
spot_order_hashes | String Array | Array with the order hashes you want to fetch in spot markets | No |
derivative_order_hashes | String Array | Array with the order hashes you want to fetch in derivative markets | No |
Response Parameters
Response Example:
{
"spotOrderStates": [
{
"orderHash": "0xb7b556d6eab10c4c185a660be44757a8a6715fb16db39708f2f76d9ce5ae8617",
"subaccountId": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"marketId": "0x0511ddc4e6586f3bfe1acb2dd905f8b8a82c97e1edaef654b12ca7e6031ca0fa",
"orderType": "limit",
"orderSide": "buy",
"state": "booked",
"quantityFilled": "0",
"quantityRemaining": "1000000",
"createdAt": 1654080262300,
"updatedAt": 1654080262300
}
],
"derivativeOrderStates": [
{
"orderHash": "0x4228f9a56a5bb50de4ceadc64df694c77e7752d58b71a7c557a27ec10e1a094e",
"subaccountId": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"marketId": "0x1c79dac019f73e4060494ab1b4fcba734350656d6fc4d474f6a238c13c6f9ced",
"orderType": "limit",
"orderSide": "buy",
"state": "booked",
"quantityFilled": "0",
"quantityRemaining": "1",
"createdAt": 1654235059957,
"updatedAt": 1654235059957
}
]
}
{
"spot_order_states": [
{
"order_hash": "0xb7b556d6eab10c4c185a660be44757a8a6715fb16db39708f2f76d9ce5ae8617",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"market_id": "0x0511ddc4e6586f3bfe1acb2dd905f8b8a82c97e1edaef654b12ca7e6031ca0fa",
"order_type": "limit",
"order_side": "buy",
"state": "booked",
"quantity_filled": "0",
"quantity_remaining": "1000000",
"created_at": 1654080262300,
"updated_at": 1654080262300
}
],
"derivative_order_states": [
{
"order_hash": "0x4228f9a56a5bb50de4ceadc64df694c77e7752d58b71a7c557a27ec10e1a094e",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"market_id": "0x1c79dac019f73e4060494ab1b4fcba734350656d6fc4d474f6a238c13c6f9ced",
"order_type": "limit",
"order_side": "buy",
"state": "booked",
"quantity_filled": "0",
"quantity_remaining": "1",
"created_at": 1654235059957,
"updated_at": 1654235059957
}
]
}
Parameter | Type | Description |
---|---|---|
spot_order_states | OrderStateRecord Array | List of the spot order state records |
derivative_order_states | OrderStateRecord Array | List of the derivative order state records |
OrderStateRecord
Parameter | Type | Description |
---|---|---|
order_hash | String | Hash of the order |
subaccount_id | String | The subaccountId that this order belongs to |
market_id | String | The Market ID of the order |
order_type | String | The type of the order |
order_side | String | The side of the order |
state | String | The order state. Should be one of: booked, partial_filled, filled, canceled |
quantity_filled | String | The filled quantity of the order |
quantity_remaining | String | The unfilled quantity of the order |
created_at | Integer | Order committed timestamp in UNIX milliseconds |
updated_at | Integer | Order updated timestamp in UNIX milliseconds |
price | String | Order price |
margin | String | Margin for derivative order |
Portfolio
Get an overview of your portfolio.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def main() -> None:
network = Network.testnet()
client = AsyncClient(network)
account_address = "inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku"
portfolio = await client.fetch_portfolio(account_address=account_address)
print(portfolio)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
)
func main() {
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
accountAddress := "inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku"
res, err := exchangeClient.GetPortfolio(ctx, accountAddress)
if err != nil {
fmt.Println(err)
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Parameter | Type | Description | Required |
---|---|---|---|
account_address | String | The Injective address | Yes |
Response Parameters
Response Example:
{
"portfolio":{
"portfolioValue":"6229.040631548905238875",
"availableBalance":"92.4500010811984646",
"lockedBalance":"13218.3573583009093604",
"unrealizedPnl":"-7081.766727833202586125",
"subaccounts":[
{
"subaccountId":"0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000002",
"availableBalance":"0",
"lockedBalance":"0",
"unrealizedPnl":"0"
},
{
"subaccountId":"0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000006",
"availableBalance":"0",
"lockedBalance":"0",
"unrealizedPnl":"0"
},
{
"subaccountId":"0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000008",
"availableBalance":"0",
"lockedBalance":"0",
"unrealizedPnl":"0"
},
{
"subaccountId":"0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000009",
"availableBalance":"0",
"lockedBalance":"0",
"unrealizedPnl":"0"
},
{
"subaccountId":"0xaf79152ac5df276d9a8e1e2e22822f971347490200000061746f6d2d75736474",
"availableBalance":"0.00000066622556",
"lockedBalance":"0",
"unrealizedPnl":"0"
},
{
"subaccountId":"0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"availableBalance":"0.0000003382963046",
"lockedBalance":"13218.3573583009093604",
"unrealizedPnl":"-7081.766727833202586125"
},
{
"subaccountId":"0xaf79152ac5df276d9a8e1e2e22822f971347490200000000696e6a2d75736474",
"availableBalance":"0.0000000766766",
"lockedBalance":"0",
"unrealizedPnl":"0"
},
{
"subaccountId":"0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000001",
"availableBalance":"92.45",
"lockedBalance":"0",
"unrealizedPnl":"0"
},
{
"subaccountId":"0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000003",
"availableBalance":"0",
"lockedBalance":"0",
"unrealizedPnl":"0"
},
{
"subaccountId":"0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000007",
"availableBalance":"0",
"lockedBalance":"0",
"unrealizedPnl":"0"
},
{
"subaccountId":"0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000004",
"availableBalance":"0",
"lockedBalance":"0",
"unrealizedPnl":"0"
},
{
"subaccountId":"0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000005",
"availableBalance":"0",
"lockedBalance":"0",
"unrealizedPnl":"0"
}
]
}
}
{
"portfolio": {
"portfolio_value": "16961.63886335580191347385",
"available_balance": "10127.8309908372442029",
"locked_balance": "8192.6038127728038576",
"unrealized_pnl": "-1358.79594025424614702615",
"subaccounts": [
{
"subaccount_id": "0x792bb0b9001d71a8efcb3c026ba4e34608a68a8c000000000000000000000000",
"available_balance": "10127.8309908372442029",
"locked_balance": "8192.6038127728038576",
"unrealized_pnl": "-1358.79594025424614702615"
}
]
}
}
Parameter | Type | Description |
---|---|---|
portfolio | AccountPortfolio | Portfolio details |
AccountPortfolio
Parameter | Type | Description |
---|---|---|
portfolio_value | String | The account's portfolio value in USD |
available_balance | String | The account's available balance value in USD |
locked_balance | String | The account's locked balance value in USD |
unrealized_pnl | String | The account's total unrealized PnL value in USD |
subaccounts | SubaccountPortfolio Array | List of all subaccounts' portfolio |
SubaccountPortfolio
Parameter | Type | Description |
---|---|---|
subaccount_id | String | The subaccount ID |
available_balance | String | The subaccount's available balance value in USD |
locked_balance | String | The subaccount's locked balance value in USD |
unrealized_pnl | String | The subaccount's total unrealized PnL value in USD |
Rewards
Get the rewards for Trade & Earn, the request will fetch all addresses for the latest epoch (-1) by default.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def main() -> None:
network = Network.testnet()
client = AsyncClient(network)
account_address = "inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku"
epoch = -1
rewards = await client.fetch_rewards(account_address=account_address, epoch=epoch)
print(rewards)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
accountPB "github.com/InjectiveLabs/sdk-go/exchange/accounts_rpc/pb"
)
func main() {
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
accountAddress := "inj1rwv4zn3jptsqs7l8lpa3uvzhs57y8duemete9e"
epoch := int64(1)
req := accountPB.RewardsRequest{
Epoch: epoch,
AccountAddress: accountAddress,
}
res, err := exchangeClient.GetRewards(ctx, &req)
if err != nil {
fmt.Println(err)
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Parameter | Type | Description | Required |
---|---|---|---|
epoch | Integer | The distribution epoch sequence number. -1 for latest | No |
account_address | String | Account address for the rewards distribution | No |
Response Parameters
Response Example:
{
"rewards":[
{
"accountAddress":"inj14au322k9munkmx5wrchz9q30juf5wjgz2cfqku",
"rewards":[
{
"denom":"inj",
"amount":"11169382212463849"
}
],
"distributedAt":"1672218001897"
}
]
}
{
"rewards": [
{
"account_address": "inj1rwv4zn3jptsqs7l8lpa3uvzhs57y8duemete9e",
"rewards": [
{
"denom": "inj",
"amount": "755104058929571177652"
}
],
"distributed_at": 1642582800716
}
]
}
Parameter | Type | Description |
---|---|---|
rewards | Reward Array | The trading rewards distributed |
Reward
Parameter | Type | Description |
---|---|---|
account_address | String | Account Injective address |
rewards | Coin Array | Reward coins distributed |
distributed_at | Integer | Rewards distribution timestamp in UNIX milliseconds |
Coin
Parameter | Type | Description |
---|---|---|
denom | String | Token denom |
amount | String | Token amount |
- InjectiveSpotExchangeRPC
InjectiveSpotExchangeRPC defines the gRPC API of the Spot Exchange provider.
Market
Get details of a single spot market.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def main() -> None:
network = Network.testnet()
client = AsyncClient(network)
market_id = "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe"
market = await client.fetch_spot_market(market_id=market_id)
print(market)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
)
func main() {
// network := common.LoadNetwork("mainnet", "k8s")
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketId := "0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0"
res, err := exchangeClient.GetSpotMarket(ctx, marketId)
if err != nil {
fmt.Println(err)
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Parameter | Type | Description | Required |
---|---|---|---|
market_id | String | MarketId of the market we want to fetch | Yes |
Response Parameters
Response Example:
{
"market":{
"marketId":"0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"marketStatus":"active",
"ticker":"INJ/USDT",
"baseDenom":"inj",
"baseTokenMeta":{
"name":"Injective Protocol",
"address":"0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30",
"symbol":"INJ",
"logo":"https://static.alchemyapi.io/images/assets/7226.png",
"decimals":18,
"updatedAt":"1683119359318"
},
"quoteDenom":"peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5",
"quoteTokenMeta":{
"name":"Testnet Tether USDT",
"address":"0x0000000000000000000000000000000000000000",
"symbol":"USDT",
"logo":"https://static.alchemyapi.io/images/assets/825.png",
"decimals":6,
"updatedAt":"1683119359320"
},
"makerFeeRate":"-0.0001",
"takerFeeRate":"0.001",
"serviceProviderFee":"0.4",
"minPriceTickSize":"0.000000000000001",
"minQuantityTickSize":"1000000000000000",
"minNotional": "1000000"
}
}
{
"market": {
"market_id": "0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0",
"market_status": "active",
"ticker": "INJ/USDT",
"base_denom": "inj",
"base_token_meta": {
"name": "Injective Protocol",
"address": "0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30",
"symbol": "INJ",
"logo": "https://static.alchemyapi.io/images/assets/7226.png",
"decimals": 18,
"updated_at": 1650978921934
},
"quote_denom": "peggy0xdAC17F958D2ee523a2206206994597C13D831ec7",
"maker_fee_rate": "0.001",
"taker_fee_rate": "0.002",
"service_provider_fee": "0.4",
"min_price_tick_size": "0.000000000000001",
"min_quantity_tick_size": "1000000000000000",
"min_notional": "1000000"
}
}
Parameter | Type | Description |
---|---|---|
market | SpotMarketInfo | Info about particular spot market |
SpotMarketInfo
Parameter | Type | Description |
---|---|---|
base_denom | String | Coin denom of the base asset |
market_id | String | ID of the spot market of interest |
market_status | String | The status of the market (Should be one of: ["active", "paused", "suspended", "demolished", "expired"]) |
min_quantity_tick_size | String | Defines the minimum required tick size for the order's quantity |
quote_token_meta | TokenMeta | Token metadata for quote asset, only for Ethereum-based assets |
service_provider_fee | String | Percentage of the transaction fee shared with the service provider |
base_token_meta | TokenMeta | Token metadata for base asset, only for Ethereum-based assets |
maker_fee_rate | String | Defines the fee percentage makers pay (or receive, if negative) in quote asset when trading |
min_price_tick_size | String | Defines the minimum required tick size for the order's price |
quote_denom | String | Coin denom of the quote asset |
taker_fee_rate | String | Defines the fee percentage takers pay (in the quote asset) when trading |
ticker | String | A name of the pair in format AAA/BBB, where AAA is base asset, BBB is quote asset |
min_notional | String | Defines the minimum required notional for an order to be accepted |
TokenMeta
Parameter | Type | Description |
---|---|---|
address | String | Token's Ethereum contract address |
decimals | Integer | Token decimals |
logo | String | URL to the logo image |
name | String | Token full name |
symbol | String | Token symbol short name |
updatedAt | Integer | Token metadata fetched timestamp in UNIX millis |
Markets
Get a list of spot markets.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def main() -> None:
network = Network.testnet()
client = AsyncClient(network)
market_status = "active"
base_denom = "inj"
quote_denom = "peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5"
market = await client.fetch_spot_markets(
market_statuses=[market_status], base_denom=base_denom, quote_denom=quote_denom
)
print(market)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
spotExchangePB "github.com/InjectiveLabs/sdk-go/exchange/spot_exchange_rpc/pb"
)
func main() {
// network := common.LoadNetwork("mainnet", "k8s")
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketStatus := "active"
quoteDenom := "peggy0xdAC17F958D2ee523a2206206994597C13D831ec7"
req := spotExchangePB.MarketsRequest{
MarketStatus: marketStatus,
QuoteDenom: quoteDenom,
}
res, err := exchangeClient.GetSpotMarkets(ctx, &req)
if err != nil {
fmt.Println(err)
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Parameter | Type | Description | Required |
---|---|---|---|
market_statuses | String Array | Filter by status of the market (Should be any of: ["active", "paused", "suspended", "demolished", "expired"]) | No |
base_denom | String | Filter by the Coin denomination of the base currency | No |
quote_denom | String | Filter by the Coin denomination of the quote currency | No |
Response Parameters
Response Example:
{
"markets":[
{
"marketId":"0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"marketStatus":"active",
"ticker":"INJ/USDT",
"baseDenom":"inj",
"baseTokenMeta":{
"name":"Injective Protocol",
"address":"0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30",
"symbol":"INJ",
"logo":"https://static.alchemyapi.io/images/assets/7226.png",
"decimals":18,
"updatedAt":"1683119359318"
},
"quoteDenom":"peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5",
"quoteTokenMeta":{
"name":"Testnet Tether USDT",
"address":"0x0000000000000000000000000000000000000000",
"symbol":"USDT",
"logo":"https://static.alchemyapi.io/images/assets/825.png",
"decimals":6,
"updatedAt":"1683119359320"
},
"makerFeeRate":"-0.0001",
"takerFeeRate":"0.001",
"serviceProviderFee":"0.4",
"minPriceTickSize":"0.000000000000001",
"minQuantityTickSize":"1000000000000000",
"minNotional":"1000000"
}
]
}
{
"markets": [
{
"market_id": "0x01edfab47f124748dc89998eb33144af734484ba07099014594321729a0ca16b",
"market_status": "active",
"ticker": "AAVE/USDT",
"base_denom": "peggy0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9",
"base_token_meta": {
"name": "Aave",
"address": "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9",
"symbol": "AAVE",
"logo": "https://static.alchemyapi.io/images/assets/7278.png",
"decimals": 18,
"updated_at": 1650978921846
},
"quote_denom": "peggy0xdAC17F958D2ee523a2206206994597C13D831ec7",
"maker_fee_rate": "0.001",
"taker_fee_rate": "0.002",
"service_provider_fee": "0.4",
"min_price_tick_size": "0.000000000000001",
"min_quantity_tick_size": "1000000000000000"
},
{
"market_id": "0xe8bf0467208c24209c1cf0fd64833fa43eb6e8035869f9d043dbff815ab76d01",
"market_status": "active",
"ticker": "UNI/USDT",
"base_denom": "peggy0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984",
"base_token_meta": {
"name": "Uniswap",
"address": "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984",
"symbol": "UNI",
"logo": "https://static.alchemyapi.io/images/assets/7083.png",
"decimals": 18,
"updated_at": 1650978922133
},
"quote_denom": "peggy0xdAC17F958D2ee523a2206206994597C13D831ec7",
"maker_fee_rate": "0.001",
"taker_fee_rate": "0.002",
"service_provider_fee": "0.4",
"min_price_tick_size": "0.000000000000001",
"min_quantity_tick_size": "1000000000000000",
"min_notional": "1000000"
}
]
}
Parameter | Type | Description |
---|---|---|
markets | SpotMarketInfo Array | List of spot markets |
SpotMarketInfo
Parameter | Type | Description |
---|---|---|
base_denom | String | Coin denom of the base asset |
market_id | String | ID of the spot market of interest |
market_status | String | The status of the market (Should be one of: ["active", "paused", "suspended", "demolished", "expired"]) |
min_quantity_tick_size | String | Defines the minimum required tick size for the order's quantity |
quote_token_meta | TokenMeta | Token metadata for quote asset, only for Ethereum-based assets |
service_provider_fee | String | Percentage of the transaction fee shared with the service provider |
base_token_meta | TokenMeta | Token metadata for base asset, only for Ethereum-based assets |
maker_fee_rate | String | Defines the fee percentage makers pay (or receive, if negative) in quote asset when trading |
min_price_tick_size | String | Defines the minimum required tick size for the order's price |
quote_denom | String | Coin denom of the quote asset |
taker_fee_rate | String | Defines the fee percentage takers pay (in the quote asset) when trading |
ticker | String | A name of the pair in format AAA/BBB, where AAA is base asset, BBB is quote asset |
min_notional | String | Defines the minimum required notional for an order to be accepted |
TokenMeta
Parameter | Type | Description |
---|---|---|
address | String | Token's Ethereum contract address |
decimals | Integer | Token decimals |
logo | String | URL to the logo image |
name | String | Token full name |
symbol | String | Token symbol short name |
updatedAt | Integer | Token metadata fetched timestamp in UNIX millis |
StreamMarkets
Stream live updates of spot markets.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from typing import Any, Dict
from grpc import RpcError
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def market_event_processor(event: Dict[str, Any]):
print(event)
def stream_error_processor(exception: RpcError):
print(f"There was an error listening to spot markets updates ({exception})")
def stream_closed_processor():
print("The spot markets updates stream has been closed")
async def main() -> None:
# select network: local, testnet, mainnet
network = Network.mainnet()
client = AsyncClient(network)
task = asyncio.get_event_loop().create_task(
client.listen_spot_markets_updates(
callback=market_event_processor,
on_end_callback=stream_closed_processor,
on_status_callback=stream_error_processor,
)
)
await asyncio.sleep(delay=60)
task.cancel()
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
)
func main() {
// network := common.LoadNetwork("mainnet", "k8s")
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketIds := []string{"0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0"}
stream, err := exchangeClient.StreamSpotMarket(ctx, marketIds)
if err != nil {
panic(err)
}
for {
select {
case <-ctx.Done():
return
default:
res, err := stream.Recv()
if err != nil {
fmt.Println(err)
return
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
}
}
Parameter | Type | Description | Required |
---|---|---|---|
market_ids | String Array | List of market IDs for updates streaming, empty means 'ALL' spot markets | No |
Response Parameters
Streaming Response Example:
{
"market":{
"marketId":"0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0",
"marketStatus":"active",
"ticker":"INJ/USDT",
"baseDenom":"inj",
"baseTokenMeta":{
"name":"Injective Protocol",
"address":"0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30",
"symbol":"INJ",
"logo":"https://static.alchemyapi.io/images/assets/7226.png",
"decimals":18,
"updatedAt":1632535055751
},
"quoteDenom":"peggy0x69efCB62D98f4a6ff5a0b0CFaa4AAbB122e85e08",
"quoteTokenMeta":{
"name":"Tether",
"address":"0x69efCB62D98f4a6ff5a0b0CFaa4AAbB122e85e08",
"symbol":"USDT",
"logo":"https://static.alchemyapi.io/images/assets/825.png",
"decimals":6,
"updatedAt":1632535055759
},
"makerFeeRate":"0.001",
"takerFeeRate":"0.002",
"serviceProviderRate":"0.4",
"minPriceTickSize":"0.000000000000001",
"minQuantityTickSize":"1000000000000000",
"minNotional":"0"
},
"operationType":"update",
"timestamp":1632535055790
}
{
"market": {
"market_id": "0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0",
"market_status": "active",
"ticker": "INJ/USDT",
"base_denom": "inj",
"base_token_meta": {
"name": "Injective Protocol",
"address": "0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30",
"symbol": "INJ",
"logo": "https://static.alchemyapi.io/images/assets/7226.png",
"decimals": 18,
"updated_at": 1632535055751
},
"quote_denom": "peggy0x69efCB62D98f4a6ff5a0b0CFaa4AAbB122e85e08",
"quote_token_meta": {
"name": "Tether",
"address": "0x69efCB62D98f4a6ff5a0b0CFaa4AAbB122e85e08",
"symbol": "USDT",
"logo": "https://static.alchemyapi.io/images/assets/825.png",
"decimals": 6,
"updated_at": 1632535055759
},
"maker_fee_rate": "0.001",
"taker_fee_rate": "0.002",
"service_provider_fee": "0.4",
"min_price_tick_size": "0.000000000000001",
"min_quantity_tick_size": "1000000000000000",
"min_notional": "0",
},
"operation_type": "update",
"timestamp": 1632535055790
}
Parameter | Type | Description |
---|---|---|
market | SpotMarketInfo | Info about particular spot market |
operation_type | String | Update type (Should be one of: ["insert", "replace", "update", "invalidate"]) |
timestamp | Integer | Operation timestamp in UNIX millis |
SpotMarketInfo
Parameter | Type | Description |
---|---|---|
base_denom | String | Coin denom of the base asset |
market_id | String | ID of the spot market of interest |
market_status | String | The status of the market (Should be one of: ["active", "paused", "suspended", "demolished", "expired"]) |
min_quantity_tick_size | String | Defines the minimum required tick size for the order's quantity |
quote_token_meta | TokenMeta | Token metadata for quote asset, only for Ethereum-based assets |
service_provider_fee | String | Percentage of the transaction fee shared with the service provider |
base_token_meta | TokenMeta | Token metadata for base asset, only for Ethereum-based assets |
maker_fee_rate | String | Defines the fee percentage makers pay (or receive, if negative) in quote asset when trading |
min_price_tick_size | String | Defines the minimum required tick size for the order's price |
quote_denom | String | Coin denom of the quote asset |
taker_fee_rate | String | Defines the fee percentage takers pay (in the quote asset) when trading |
ticker | String | A name of the pair in format AAA/BBB, where AAA is base asset, BBB is quote asset |
min_notional | String | Defines the minimum required notional for an order to be accepted |
TokenMeta
Parameter | Type | Description |
---|---|---|
address | String | Token's Ethereum contract address |
decimals | Integer | Token decimals |
logo | String | URL to the logo image |
name | String | Token full name |
symbol | String | Token symbol short name |
updatedAt | Integer | Token metadata fetched timestamp in UNIX millis |
OrdersHistory
List history of orders (all states) for a spot market.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.client.model.pagination import PaginationOption
from pyinjective.core.network import Network
async def main() -> None:
network = Network.testnet()
client = AsyncClient(network)
market_ids = ["0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe"]
subaccount_id = "0xbdaedec95d563fb05240d6e01821008454c24c36000000000000000000000000"
skip = 10
limit = 3
order_types = ["buy_po"]
pagination = PaginationOption(skip=skip, limit=limit)
orders = await client.fetch_spot_orders_history(
subaccount_id=subaccount_id,
market_ids=market_ids,
order_types=order_types,
pagination=pagination,
)
print(orders)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
spotExchangePB "github.com/InjectiveLabs/sdk-go/exchange/spot_exchange_rpc/pb"
)
func main() {
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketId := "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe"
subaccountId := "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000"
skip := uint64(0)
limit := int32(10)
orderTypes := []string{"buy_po"}
req := spotExchangePB.OrdersHistoryRequest{
SubaccountId: subaccountId,
MarketId: marketId,
Skip: skip,
Limit: limit,
OrderTypes: orderTypes,
}
res, err := exchangeClient.GetHistoricalSpotOrders(ctx, &req)
if err != nil {
panic(err)
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Parameter | Type | Description | Required |
---|---|---|---|
subaccount_id | String | Filter by subaccount ID | No |
market_ids | String Array | Filter by multiple market IDs | No |
order_types | String Array | The order types to be included (Should be one of: ["buy", "sell", "stop_buy", "stop_sell", "take_buy", "take_sell", "buy_po", "sell_po"]) | No |
direction | String | Filter by order direction (Should be one of: ["buy", "sell"]) | No |
state | String | The order state (Should be one of: ["booked", "partial_filled", "filled", "canceled"]) | No |
execution_types | String Array | The execution of the order (Should be one of: ["limit", "market"]) | No |
trade_id | String | Filter by the trade's trade id | No |
active_markets_only | Bool | Return only orders for active markets | No |
cid | String | Filter by the custom client order id of the trade's order | No |
pagination | PaginationOption | Pagination configuration | No |
Response Parameters
Response Example:
{
"orders":[
{
"orderHash":"0x4e6629ce45597a3dc3941c5382cc7bc542d52fbcc6b03c4fd604c94a9bec0cc1",
"marketId":"0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"subaccountId":"0xbdaedec95d563fb05240d6e01821008454c24c36000000000000000000000000",
"executionType":"limit",
"orderType":"buy_po",
"price":"0.000000000000001",
"triggerPrice":"0",
"quantity":"1000000000000000",
"filledQuantity":"1000000000000000",
"state":"filled",
"createdAt":"1668264339149",
"updatedAt":"1682667017745",
"direction":"buy",
"txHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"isActive":false,
"cid":""
},
{
"orderHash":"0x347de654c8484fe36473c3569382ff27d25e95c660fd055163b7193607867a8b",
"marketId":"0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"subaccountId":"0xbdaedec95d563fb05240d6e01821008454c24c36000000000000000000000000",
"executionType":"limit",
"orderType":"buy_po",
"price":"0.000000000000001",
"triggerPrice":"0",
"quantity":"1000000000000000",
"filledQuantity":"1000000000000000",
"state":"filled",
"createdAt":"1668264339149",
"updatedAt":"1682667017745",
"direction":"buy",
"txHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"isActive":false,
"cid":""
},
{
"orderHash":"0x2141d52714f5c9328170cc674de8ecf876463b1999bea4124d1de595152b718f",
"marketId":"0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"subaccountId":"0xbdaedec95d563fb05240d6e01821008454c24c36000000000000000000000000",
"executionType":"limit",
"orderType":"buy_po",
"price":"0.000000000000001",
"triggerPrice":"0",
"quantity":"1000000000000000",
"filledQuantity":"1000000000000000",
"state":"filled",
"createdAt":"1668264339149",
"updatedAt":"1682667017745",
"direction":"buy",
"txHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"isActive":false,
"cid":""
}
],
"paging":{
"total":"1000",
"from":0,
"to":0,
"countBySubaccount":"0",
"next":[
]
}
}
{
"orders": [
{
"order_hash": "0x47a3858df766691a6124255a959ac17c79588fa36e52bed6d8aea2d927bb6a60",
"market_id": "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"execution_type": "limit",
"order_type": "buy_po",
"price": "0.000000000007789",
"trigger_price": "0",
"quantity": "12000000000000000000",
"filled_quantity": "12000000000000000000",
"state": "filled",
"created_at": 1681812187591,
"updated_at": 1681886620984,
"direction": "buy"
},
{
"order_hash": "0x4a0f7bec21c2861ec390510f461ab94a6e4425453e113ba41d67c5e79a45538b",
"market_id": "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"execution_type": "limit",
"order_type": "buy_po",
"price": "0.000000000007692",
"trigger_price": "0",
"quantity": "14000000000000000000",
"filled_quantity": "14000000000000000000",
"state": "filled",
"created_at": 1681812187591,
"updated_at": 1681886620984,
"direction": "buy"
},
{
"order_hash": "0x447b593a3c1683b64bd6ac4e60aa6ff22078951312eb3bfacf0b8b163eb015e4",
"market_id": "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"execution_type": "limit",
"order_type": "buy_po",
"price": "0.000000000005787",
"trigger_price": "0",
"quantity": "18000000000000000000",
"filled_quantity": "18000000000000000000",
"state": "filled",
"created_at": 1681812187591,
"updated_at": 1681886620984,
"direction": "buy"
},
{
"order_hash": "0x77d1c86d0b04b3347ace0f4a7f708adbb160d54701891d0c212a8c28bb10f77f",
"market_id": "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"execution_type": "limit",
"order_type": "buy_po",
"price": "0.000000000005457",
"trigger_price": "0",
"quantity": "8000000000000000000",
"filled_quantity": "8000000000000000000",
"state": "filled",
"created_at": 1681812187591,
"updated_at": 1681886620984,
"direction": "buy"
},
{
"order_hash": "0x76899c13fa3e591b1e2cbadfc2c84db5a7f4f97e42cee2451a6a90d04b100642",
"market_id": "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"execution_type": "limit",
"order_type": "buy_po",
"price": "0.000000000007134",
"trigger_price": "0",
"quantity": "4000000000000000000",
"filled_quantity": "4000000000000000000",
"state": "filled",
"created_at": 1681812187591,
"updated_at": 1681886620984,
"direction": "buy"
},
{
"order_hash": "0xf353711353a98ac3aceee62a4d7fed30e0c65cf38adfa898c455be5e5c671445",
"market_id": "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"execution_type": "limit",
"order_type": "buy_po",
"price": "0.000000000006138",
"trigger_price": "0",
"quantity": "2000000000000000000",
"filled_quantity": "2000000000000000000",
"state": "filled",
"created_at": 1681812187591,
"updated_at": 1681886620984,
"direction": "buy"
},
{
"order_hash": "0xb599db2124630b350e0ca2ea3453ece84e7721334e1009b451fa21d072a6cf8f",
"market_id": "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"execution_type": "limit",
"order_type": "buy_po",
"price": "0.000000000005667",
"trigger_price": "0",
"quantity": "22000000000000000000",
"filled_quantity": "22000000000000000000",
"state": "filled",
"created_at": 1681812187591,
"updated_at": 1681886620984,
"direction": "buy"
},
{
"order_hash": "0x1c28300cfebfef73c26e32d396162e45089e34a5ba0c627cc8b6e3fb1d9861ad",
"market_id": "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"execution_type": "limit",
"order_type": "buy_po",
"price": "0.000000000006263",
"trigger_price": "0",
"quantity": "20000000000000000000",
"filled_quantity": "20000000000000000000",
"state": "filled",
"created_at": 1681812187591,
"updated_at": 1681886620984,
"direction": "buy"
},
{
"order_hash": "0x7a2b9753c94c67f5e79e2f9dcd8af8a619d55d2f9ba1a134a22c5ef154b76e7f",
"market_id": "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"execution_type": "limit",
"order_type": "buy_po",
"price": "0.000000000007683",
"trigger_price": "0",
"quantity": "16000000000000000000",
"filled_quantity": "16000000000000000000",
"state": "filled",
"created_at": 1681812187591,
"updated_at": 1681886620984,
"direction": "buy"
},
{
"order_hash": "0x4984a08abefd29ba6bc914b11182251e18c0235842916955a4ffdc8ff149d188",
"market_id": "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"execution_type": "limit",
"order_type": "buy_po",
"price": "0.000000000007668",
"trigger_price": "0",
"quantity": "6000000000000000000",
"filled_quantity": "6000000000000000000",
"state": "filled",
"created_at": 1681812187591,
"updated_at": 1681886620984,
"direction": "buy"
}
],
"paging": {
"total": 1000
}
}
Parameter | Type | Description |
---|---|---|
orders | SpotOrderHistory Array | List of prior spot orders |
paging | Paging | Pagination of results |
SpotOrderHistory
Parameter | Type | Description |
---|---|---|
order_hash | String | Hash of the order |
market_id | String | ID of the spot market |
is_active | Boolean | Indicates if the order is active |
subaccount_id | String | ID of the subaccount that the order belongs to |
execution_type | String | The type of the order (Should be one of: ["limit", "market"]) |
order_type | String | Order type (Should be one of: ["buy", "sell", "stop_buy", "stop_sell", "take_buy", "take_sell", "buy_po", "sell_po"]) |
price | String | Price of the order |
trigger_price | String | Trigger price used by stop/take orders |
quantity | String | Quantity of the order |
filled_quantity | String | The amount of the quantity filled |
state | String | Order state (Should be one of: ["booked", "partial_filled", "filled", "canceled"]) |
created_at | Integer | Order created timestamp in UNIX millis |
updated_at | Integer | Order updated timestamp in UNIX millis |
direction | String | The direction of the order (Should be one of: ["buy", "sell"]) |
tx_hash | String | Transaction hash in which the order was created (not all orders have this value) |
cid | String | Identifier for the order specified by the user (up to 36 characters, like a UUID) |
Paging
Parameter | Type | Description |
---|---|---|
total | Integer | Total number of available records |
StreamOrdersHistory
Stream order updates for spot markets. If no parameters are given, updates to all subaccounts in all spot markets will be streamed.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from typing import Any, Dict
from grpc import RpcError
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def order_event_processor(event: Dict[str, Any]):
print(event)
def stream_error_processor(exception: RpcError):
print(f"There was an error listening to spot orders history updates ({exception})")
def stream_closed_processor():
print("The spot orders history updates stream has been closed")
async def main() -> None:
network = Network.testnet()
client = AsyncClient(network)
market_id = "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe"
order_direction = "buy"
task = asyncio.get_event_loop().create_task(
client.listen_spot_orders_history_updates(
callback=order_event_processor,
on_end_callback=stream_closed_processor,
on_status_callback=stream_error_processor,
market_id=market_id,
direction=order_direction,
)
)
await asyncio.sleep(delay=60)
task.cancel()
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
spotExchangePB "github.com/InjectiveLabs/sdk-go/exchange/spot_exchange_rpc/pb"
)
func main() {
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketId := "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe"
subaccountId := "0xc6fe5d33615a1c52c08018c47e8bc53646a0e101000000000000000000000000"
direction := "buy"
req := spotExchangePB.StreamOrdersHistoryRequest{
MarketId: marketId,
SubaccountId: subaccountId,
Direction: direction,
}
stream, err := exchangeClient.StreamHistoricalSpotOrders(ctx, &req)
if err != nil {
panic(err)
}
for {
select {
case <-ctx.Done():
return
default:
res, err := stream.Recv()
if err != nil {
panic(err)
return
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
}
}
Parameter | Type | Description | Required |
---|---|---|---|
market_id | String | Filter by market ID | No |
subaccount_id | String | Filter by subaccount ID | No |
direction | String | Filter by direction (Should be one of: ["buy", "sell"]) | No |
state | String | Filter by state (Should be one of: ["booked", "partial_filled", "filled", "canceled"]) | No |
order_types | String Array | Filter by order type (Should be one of: ["buy", "sell", "stop_buy", "stop_sell", "take_buy", "take_sell", "buy_po", "sell_po"]) | No |
execution_types | String Array | Filter by execution type (Should be one of: ["limit", "market"]) | No |
callback | Function | Function receiving one parameter (a stream event JSON dictionary) to process each new event | Yes |
on_end_callback | Function | Function with the logic to execute when the stream connection is interrupted | No |
on_status_callback | Function | Function receiving one parameter (the exception) with the logic to execute when an exception happens | No |
Response Parameters
Streaming Response Example:
{
"order":{
"orderHash":"0xff6a1ce6339911bb6f0765e17e70144ae62834e65e551e910018203d62bc6d12",
"marketId":"0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"subaccountId":"0x5e249f0e8cb406f41de16e1bd6f6b55e7bc75add000000000000000000000004",
"executionType":"limit",
"orderType":"buy_po",
"price":"0.000000000019028",
"triggerPrice":"0",
"quantity":"67129093000000000000000",
"filledQuantity":"0",
"state":"canceled",
"createdAt":"1702044186286",
"updatedAt":"1702044188683",
"direction":"buy",
"txHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"isActive":false,
"cid":""
},
"operationType":"update",
"timestamp":"1702044191000"
}
{
"order": {
"order_hash": "0xf8a90ee4cfb4c938035b791d3b3561e8991803793b4b5590164b2ecbfa247f3d",
"market_id": "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"subaccount_id": "0x5e249f0e8cb406f41de16e1bd6f6b55e7bc75add000000000000000000000004",
"execution_type": "limit",
"order_type": "buy_po",
"price": "0.000000000007438",
"trigger_price": "0",
"quantity": "76848283000000000000000",
"filled_quantity": "0",
"state": "canceled",
"created_at": 1696621893030,
"updated_at": 1696621895445,
"direction": "buy"
},
"operation_type": "update",
"timestamp": 1696621898000
}{
"order": {
"order_hash": "0xfd6bf489944cb181ee94057b80ffdfc113a17d48d0455c8d10e4deadf341bdfd",
"market_id": "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"subaccount_id": "0x5e249f0e8cb406f41de16e1bd6f6b55e7bc75add000000000000000000000004",
"execution_type": "limit",
"order_type": "buy_po",
"price": "0.000000000007478",
"trigger_price": "0",
"quantity": "76437220000000000000000",
"filled_quantity": "0",
"state": "canceled",
"created_at": 1696621893030,
"updated_at": 1696621895445,
"direction": "buy"
},
"operation_type": "update",
"timestamp": 1696621898000
}
Parameter | Type | Description |
---|---|---|
order | SpotOrderHistory | Updated Order |
operation_type | String | Order update type (Should be one of: ["insert", "replace", "update", "invalidate"]) |
timestamp | Integer | Operation timestamp in UNIX millis |
SpotOrderHistory
Parameter | Type | Description |
---|---|---|
order_hash | String | Hash of the order |
market_id | String | ID of the spot market |
is_active | Boolean | Indicates if the order is active |
subaccount_id | String | ID of the subaccount that the order belongs to |
execution_type | String | The type of the order (Should be one of: ["limit", "market"]) |
order_type | String | Order type (Should be one of: ["buy", "sell", "stop_buy", "stop_sell", "take_buy", "take_sell", "buy_po", "sell_po"]) |
price | String | Price of the order |
trigger_price | String | Trigger price used by stop/take orders |
quantity | String | Quantity of the order |
filled_quantity | String | The amount of the quantity filled |
state | String | Order state (Should be one of: ["booked", "partial_filled", "filled", "canceled"]) |
created_at | Integer | Order created timestamp in UNIX millis |
updated_at | Integer | Order updated timestamp in UNIX millis |
direction | String | The direction of the order (Should be one of: ["buy", "sell"]) |
tx_hash | String | Transaction hash in which the order was created (not all orders have this value) |
cid | String | Identifier for the order specified by the user (up to 36 characters, like a UUID) |
TradesV2
Get trade history for a spot market. The default request returns all spot trades from all markets.
IP rate limit group: indexer
*Trade execution types
"market"
for market orders"limitFill"
for a resting limit order getting filled by a market order"limitMatchRestingOrder"
for a resting limit order getting matched with another new limit order"limitMatchNewOrder"
for a new limit order getting matched immediately
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def main() -> None:
network = Network.testnet()
client = AsyncClient(network)
market_ids = ["0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe"]
execution_side = "taker"
direction = "buy"
subaccount_ids = ["0xc7dca7c15c364865f77a4fb67ab11dc95502e6fe000000000000000000000001"]
execution_types = ["limitMatchNewOrder", "market"]
orders = await client.fetch_spot_trades(
market_ids=market_ids,
subaccount_ids=subaccount_ids,
execution_side=execution_side,
direction=direction,
execution_types=execution_types,
)
print(orders)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
spotExchangePB "github.com/InjectiveLabs/sdk-go/exchange/spot_exchange_rpc/pb"
)
func main() {
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketId := "0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0"
subaccountId := "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000"
req := spotExchangePB.TradesV2Request{
MarketId: marketId,
SubaccountId: subaccountId,
}
res, err := exchangeClient.GetSpotTradesV2(ctx, &req)
if err != nil {
panic(err)
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Parameter | Type | Description | Required |
---|---|---|---|
market_ids | String Array | Filter by multiple market IDs | No |
subaccount_ids | String Array | Filter by multiple subaccount IDs | No |
execution_side | String | Filter by the execution side of the trade (Should be one of: ["maker", "taker"]) | No |
direction | String | Filter by the direction of the trade (Should be one of: ["buy", "sell"]) | No |
execution_types | String Array | Filter by the *trade execution type (Should be one of: ["market", "limitFill", "limitMatchRestingOrder", "limitMatchNewOrder"]) | No |
trade_id | String | Filter by the trade id of the trade | No |
account_address | String | Filter by the account address | No |
cid | String | Filter by the custom client order id of the trade's order | No |
pagination | PaginationOption | Pagination configuration | No |
Response Parameters
Response Example:
{
"trades":[
{
"orderHash":"0x952bb14a7a377697d724c60d6077ef3dfe894c98f854970fab187247be832b6f",
"subaccountId":"0x101411266c6e2b610b4a0324d2bfb2ef0ca6e1dd000000000000000000000000",
"marketId":"0x01edfab47f124748dc89998eb33144af734484ba07099014594321729a0ca16b",
"tradeExecutionType":"limitMatchRestingOrder",
"tradeDirection":"buy",
"price":{
"price":"0.00000000001",
"quantity":"1000000000000000000",
"timestamp":"1701961116630"
},
"fee":"-600",
"executedAt":"1701961116630",
"feeRecipient":"inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"tradeId":"1321_0",
"executionSide":"maker",
"cid":"96866b8b-02dd-4288-97d3-e5254e4888b3"
},
{
"orderHash":"0x85a824c31f59cf68235b48666c4821334813f2b80db937f02d192f1e3fc74368",
"subaccountId":"0x3db1f84431dfe4df617f9eb2d04edf432beb9826000000000000000000000000",
"marketId":"0x01edfab47f124748dc89998eb33144af734484ba07099014594321729a0ca16b",
"tradeExecutionType":"limitMatchNewOrder",
"tradeDirection":"sell",
"price":{
"price":"0.00000000001",
"quantity":"1000000000000000000",
"timestamp":"1701961116630"
},
"fee":"10000",
"executedAt":"1701961116630",
"feeRecipient":"inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"tradeId":"1321_1",
"executionSide":"taker",
"cid":"spot_AAVE/USDT"
},
{
"orderHash":"0xffabb2d12a745d79eb12c7ef0eb59c729aaa4387a141f858153c8b8f58168b2e",
"subaccountId":"0x101411266c6e2b610b4a0324d2bfb2ef0ca6e1dd000000000000000000000000",
"marketId":"0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0",
"tradeExecutionType":"limitMatchRestingOrder",
"tradeDirection":"buy",
"price":{
"price":"0.00000000001",
"quantity":"2000000000000000000",
"timestamp":"1701960607140"
},
"fee":"-2400",
"executedAt":"1701960607140",
"feeRecipient":"inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"tradeId":"646_0",
"executionSide":"maker",
"cid":"ec581735-f801-4bf3-9101-282b301bf5cd"
},
{
"orderHash":"0xa19e24eef9877ec4980b8d259c1d21fa1dafcd50691e6f853e84af74fb23c05c",
"subaccountId":"0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"marketId":"0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0",
"tradeExecutionType":"limitMatchNewOrder",
"tradeDirection":"sell",
"price":{
"price":"0.00000000001",
"quantity":"2000000000000000000",
"timestamp":"1701960607140"
},
"fee":"40000",
"executedAt":"1701960607140",
"feeRecipient":"inj1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8dkncm8",
"tradeId":"646_1",
"executionSide":"taker",
"cid":""
},
{
"orderHash":"0xffabb2d12a745d79eb12c7ef0eb59c729aaa4387a141f858153c8b8f58168b2e",
"subaccountId":"0x101411266c6e2b610b4a0324d2bfb2ef0ca6e1dd000000000000000000000000",
"marketId":"0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0",
"tradeExecutionType":"limitMatchRestingOrder",
"tradeDirection":"buy",
"price":{
"price":"0.00000000001",
"quantity":"8000000000000000000",
"timestamp":"1701960594997"
},
"fee":"-9600",
"executedAt":"1701960594997",
"feeRecipient":"inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"tradeId":"630_0",
"executionSide":"maker",
"cid":"ec581735-f801-4bf3-9101-282b301bf5cd"
},
{
"orderHash":"0x87b786072190a2f38e9057987be7bdcb4e2274a6c16fdb9670e5c2ded765140f",
"subaccountId":"0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"marketId":"0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0",
"tradeExecutionType":"limitMatchNewOrder",
"tradeDirection":"sell",
"price":{
"price":"0.00000000001",
"quantity":"8000000000000000000",
"timestamp":"1701960594997"
},
"fee":"160000",
"executedAt":"1701960594997",
"feeRecipient":"inj1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8dkncm8",
"tradeId":"630_1",
"executionSide":"taker",
"cid":""
}
],
"paging":{
"total":"6",
"from":1,
"to":6,
"countBySubaccount":"0",
"next":[
]
}
}
{
"trades": [
{
"order_hash": "0xffabb2d12a745d79eb12c7ef0eb59c729aaa4387a141f858153c8b8f58168b2e",
"subaccount_id": "0x101411266c6e2b610b4a0324d2bfb2ef0ca6e1dd000000000000000000000000",
"market_id": "0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0",
"trade_execution_type": "limitMatchRestingOrder",
"trade_direction": "buy",
"price": {
"price": "0.00000000001",
"quantity": "2000000000000000000",
"timestamp": 1701960607140
},
"fee": "-2400",
"executed_at": 1701960607140,
"fee_recipient": "inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"trade_id": "646_0",
"execution_side": "maker",
"cid": "ec581735-f801-4bf3-9101-282b301bf5cd"
},
{
"order_hash": "0xa19e24eef9877ec4980b8d259c1d21fa1dafcd50691e6f853e84af74fb23c05c",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"market_id": "0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0",
"trade_execution_type": "limitMatchNewOrder",
"trade_direction": "sell",
"price": {
"price": "0.00000000001",
"quantity": "2000000000000000000",
"timestamp": 1701960607140
},
"fee": "40000",
"executed_at": 1701960607140,
"fee_recipient": "inj1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8dkncm8",
"trade_id": "646_1",
"execution_side": "taker"
},
{
"order_hash": "0xffabb2d12a745d79eb12c7ef0eb59c729aaa4387a141f858153c8b8f58168b2e",
"subaccount_id": "0x101411266c6e2b610b4a0324d2bfb2ef0ca6e1dd000000000000000000000000",
"market_id": "0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0",
"trade_execution_type": "limitMatchRestingOrder",
"trade_direction": "buy",
"price": {
"price": "0.00000000001",
"quantity": "8000000000000000000",
"timestamp": 1701960594997
},
"fee": "-9600",
"executed_at": 1701960594997,
"fee_recipient": "inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"trade_id": "630_0",
"execution_side": "maker",
"cid": "ec581735-f801-4bf3-9101-282b301bf5cd"
},
{
"order_hash": "0x87b786072190a2f38e9057987be7bdcb4e2274a6c16fdb9670e5c2ded765140f",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"market_id": "0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0",
"trade_execution_type": "limitMatchNewOrder",
"trade_direction": "sell",
"price": {
"price": "0.00000000001",
"quantity": "8000000000000000000",
"timestamp": 1701960594997
},
"fee": "160000",
"executed_at": 1701960594997,
"fee_recipient": "inj1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8dkncm8",
"trade_id": "630_1",
"execution_side": "taker"
}
],
"paging": {
"total": 4,
"from": 1,
"to": 4
}
}
Parameter | Type | Description |
---|---|---|
trades | SpotTrade Array | Trades of a particular spot market |
paging | Paging | Pagination of results |
SpotTrade
Parameter | Type | Description |
---|---|---|
order_hash | String | The order hash |
subaccount_id | String | The subaccountId that executed the trade |
market_id | String | The ID of the market that this trade is in |
trade_execution_type | String | Execution type of the trade (Should be one of: ["market", "limitFill", "limitMatchRestingOrder", "limitMatchNewOrder"]) |
trade_direction | String | Direction of the trade(Should be one of: ["buy", "sell"]) |
price | PriceLevel | Price level at which trade has been executed |
fee | String | The fee associated with the trade (quote asset denom) |
executed_at | Integer | Timestamp of trade execution (on chain) in UNIX millis |
fee_recipient | String | The address that received 40% of the fees |
trade_id | String | Unique identifier to differentiate between trades |
execution_side | String | Execution side of trade (Should be one of: ["maker", "taker"]) |
cid | String | Identifier for the order specified by the user (up to 36 characters, like a UUID) |
PriceLevel
Parameter | Type | Description |
---|---|---|
price | String | Price number of the price level |
quantity | String | Quantity of the price level |
timestamp | Integer | Price level last updated timestamp in UNIX millis |
Paging
Parameter | Type | Description |
---|---|---|
total | Integer | Total number of records available |
StreamTradesV2
Stream newly executed trades of spot markets. The default request streams trades from all spot markets.
IP rate limit group: indexer
*Trade execution types
"market"
for market orders"limitFill"
for a resting limit order getting filled by a market order"limitMatchRestingOrder"
for a resting limit order getting matched with another new limit order"limitMatchNewOrder"
for a new limit order getting matched immediately
Request Parameters
Request Example:
import asyncio
from typing import Any, Dict
from grpc import RpcError
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def trade_event_processor(event: Dict[str, Any]):
print(event)
def stream_error_processor(exception: RpcError):
print(f"There was an error listening to spot trades updates ({exception})")
def stream_closed_processor():
print("The spot trades updates stream has been closed")
async def main() -> None:
network = Network.testnet()
client = AsyncClient(network)
market_ids = [
"0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"0x7a57e705bb4e09c88aecfc295569481dbf2fe1d5efe364651fbe72385938e9b0",
]
execution_side = "maker"
direction = "sell"
subaccount_id = "0xc7dca7c15c364865f77a4fb67ab11dc95502e6fe000000000000000000000001"
execution_types = ["limitMatchRestingOrder"]
task = asyncio.get_event_loop().create_task(
client.listen_spot_trades_updates(
callback=trade_event_processor,
on_end_callback=stream_closed_processor,
on_status_callback=stream_error_processor,
market_ids=market_ids,
subaccount_ids=[subaccount_id],
execution_side=execution_side,
direction=direction,
execution_types=execution_types,
)
)
await asyncio.sleep(delay=60)
task.cancel()
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
spotExchangePB "github.com/InjectiveLabs/sdk-go/exchange/spot_exchange_rpc/pb"
)
func main() {
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketId := "0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0"
subaccountId := "0xc6fe5d33615a1c52c08018c47e8bc53646a0e101000000000000000000000000"
req := spotExchangePB.StreamTradesV2Request{
MarketId: marketId,
SubaccountId: subaccountId,
}
stream, err := exchangeClient.StreamSpotTradesV2(ctx, &req)
if err != nil {
panic(err)
}
for {
select {
case <-ctx.Done():
return
default:
res, err := stream.Recv()
if err != nil {
fmt.Println(err)
return
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
}
}
Parameter | Type | Description | Required |
---|---|---|---|
market_ids | String Array | Filter by multiple market IDs | No |
subaccount_ids | String Array | Filter by multiple subaccount IDs | No |
execution_side | String | Filter by the execution side of the trade (Should be one of: ["maker", "taker"]) | No |
direction | String | Filter by the direction of the trade (Should be one of: ["buy", "sell"]) | No |
execution_types | String Array | Filter by the *trade execution type (Should be one of: ["market", "limitFill", "limitMatchRestingOrder", "limitMatchNewOrder"]) | No |
trade_id | String | Filter by the trade's trade id | No |
account_address | String | Filter by the account address | No |
cid | String | Filter by the custom client order id of the trade's order | No |
pagination | PaginationOption | Pagination configuration | No |
callback | Function | Function receiving one parameter (a stream event JSON dictionary) to process each new event | Yes |
on_end_callback | Function | Function with the logic to execute when the stream connection is interrupted | No |
on_status_callback | Function | Function receiving one parameter (the exception) with the logic to execute when an exception happens | No |
Response Parameters
Streaming Response Example:
{
"trade":{
"orderHash":"0xa7f4a7d85136d97108d271caadd93bf697ff965790e0e1558617b953cced4adc",
"subaccountId":"0x3db1f84431dfe4df617f9eb2d04edf432beb9826000000000000000000000000",
"marketId":"0x01edfab47f124748dc89998eb33144af734484ba07099014594321729a0ca16b",
"tradeExecutionType":"limitMatchNewOrder",
"tradeDirection":"sell",
"price":{
"price":"0.00000000001",
"quantity":"1000000000000000000",
"timestamp":"1701978102242"
},
"fee":"10000",
"executedAt":"1701978102242",
"feeRecipient":"inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"tradeId":"22868_1",
"executionSide":"taker",
"cid":"96866b8b-02dd-4288-97d3-e5254e4999d4"
},
"operationType":"insert",
"timestamp":"1701978103000"
}
{
"trade":{
"orderHash":"0x952bb14a7a377697d724c60d6077ef3dfe894c98f854970fab187247be832b6f",
"subaccountId":"0x101411266c6e2b610b4a0324d2bfb2ef0ca6e1dd000000000000000000000000",
"marketId":"0x01edfab47f124748dc89998eb33144af734484ba07099014594321729a0ca16b",
"tradeExecutionType":"limitMatchRestingOrder",
"tradeDirection":"buy",
"price":{
"price":"0.00000000001",
"quantity":"1000000000000000000",
"timestamp":"1701978102242"
},
"fee":"-600",
"executedAt":"1701978102242",
"feeRecipient":"inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"tradeId":"22868_0",
"executionSide":"maker",
"cid":"96866b8b-02dd-4288-97d3-e5254e4888b3"
},
"operationType":"insert",
"timestamp":"1701978103000"
}
{
"trade": {
"order_hash": "0x88e34872af0147f57c8c5a093c3a6a8a97358615bccf975b4a06dfb5162daeaf",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"market_id": "0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0",
"trade_execution_type": "market",
"trade_direction": "sell",
"price": {
"price": "0.000000000001654",
"quantity": "1000000000000000000",
"timestamp": 1653042087046
},
"fee": "3308",
"executed_at": 1653042087046,
"fee_recipient": "inj1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8dkncm8"
},
"operation_type": "insert",
"timestamp": 1653042089000
}{
"trade": {
"order_hash": "0xb5d651a01faa90ec53b0fa34f00f3ecdfe169f9fc35be8114ee113eea9257c30",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"market_id": "0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0",
"trade_execution_type": "market",
"trade_direction": "sell",
"price": {
"price": "0.000000000001654",
"quantity": "2000000000000000000",
"timestamp": 1653042093023
},
"fee": "6616",
"executed_at": 1653042093023,
"fee_recipient": "inj1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8dkncm8"
},
"operation_type": "insert",
"timestamp": 1653042098000
}
Parameter | Type | Description |
---|---|---|
trade | SpotTrade | New spot market trade |
operation_type | String | Trade operation type (Should be one of: ["insert", "invalidate"]) |
timestamp | Integer | Timestamp the new trade is written into the database in UNIX millis |
SpotTrade
Parameter | Type | Description |
---|---|---|
order_hash | String | The order hash |
subaccount_id | String | The subaccountId that executed the trade |
market_id | String | The ID of the market that this trade is in |
trade_execution_type | String | Execution type of the trade (Should be one of: ["market", "limitFill", "limitMatchRestingOrder", "limitMatchNewOrder"]) |
trade_direction | String | Direction of the trade(Should be one of: ["buy", "sell"]) |
price | PriceLevel | Price level at which trade has been executed |
fee | String | The fee associated with the trade (quote asset denom) |
executed_at | Integer | Timestamp of trade execution (on chain) in UNIX millis |
fee_recipient | String | The address that received 40% of the fees |
trade_id | String | Unique identifier to differentiate between trades |
execution_side | String | Execution side of trade (Should be one of: ["maker", "taker"]) |
cid | String | Identifier for the order specified by the user (up to 36 characters, like a UUID) |
PriceLevel
Parameter | Type | Description |
---|---|---|
price | String | Price number of the price level |
quantity | String | Quantity of the price level |
timestamp | Integer | Price level last updated timestamp in UNIX millis |
OrderbooksV2
Get an orderbook snapshot for one or more spot markets.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def main() -> None:
network = Network.testnet()
client = AsyncClient(network)
market_ids = [
"0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"0x7a57e705bb4e09c88aecfc295569481dbf2fe1d5efe364651fbe72385938e9b0",
]
orderbooks = await client.fetch_spot_orderbooks_v2(market_ids=market_ids)
print(orderbooks)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
)
func main() {
// network := common.LoadNetwork("mainnet", "k8s")
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketIds := []string{"0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0"}
res, err := exchangeClient.GetSpotOrderbooksV2(ctx, marketIds)
if err != nil {
fmt.Println(err)
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Parameter | Type | Description | Required |
---|---|---|---|
market_ids | String Array | List of IDs of markets to get orderbook snapshots from | Yes |
Response Parameters
Response Example:
{
"orderbooks":[
{
"marketId":"0x7a57e705bb4e09c88aecfc295569481dbf2fe1d5efe364651fbe72385938e9b0",
"orderbook":{
"sells":[
{
"price":"0.000000000005",
"quantity":"27767884000000000000000",
"timestamp":"1694702425539"
},
{
"price":"0.0000000000045",
"quantity":"3519999000000000000000000",
"timestamp":"1694424758707"
}
],
"timestamp":"-62135596800000",
"buys":[
],
"sequence":"0"
}
},
{
"marketId":"0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"orderbook":{
"buys":[
{
"price":"0.000000000073489",
"quantity":"129000000000000000",
"timestamp":"1702042963690"
},
{
"price":"0.000000000064261",
"quantity":"1292000000000000000",
"timestamp":"1702039612697"
}
],
"sells":[
{
"price":"0.000000000085",
"quantity":"6693248000000000000000",
"timestamp":"1702044317059"
},
{
"price":"0.000000000085768",
"quantity":"581000000000000000",
"timestamp":"1701944786578"
}
],
"sequence":"6916386",
"timestamp":"1702044336800"
}
}
]
}
Parameter | Type | Description |
---|---|---|
orderbooks | SingleSpotLimitOrderbookV2 Array | List of spot market orderbooks with market IDs |
SingleSpotLimitOrderbookV2
Parameter | Type | Description |
---|---|---|
market_id | String | ID of spot market |
orderbook | SpotLimitOrderBookV2 | Orderbook of the market |
SpotLimitOrderbookV2
Parameter | Type | Description |
---|---|---|
buys | PriceLevel Array | List of price levels for buys |
sells | PriceLevel Array | List of price levels for sells |
sequence | Integer | Sequence number of the orderbook; increments by 1 each update |
PriceLevel
Parameter | Type | Description |
---|---|---|
price | String | Price number of the price level |
quantity | String | Quantity of the price level |
timestamp | Integer | Price level last updated timestamp in UNIX millis |
StreamOrderbooksV2
Stream orderbook snapshot updates for one or more spot markets.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from typing import Any, Dict
from grpc import RpcError
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def orderbook_event_processor(event: Dict[str, Any]):
print(event)
def stream_error_processor(exception: RpcError):
print(f"There was an error listening to spot orderbook snapshots ({exception})")
def stream_closed_processor():
print("The spot orderbook snapshots stream has been closed")
async def main() -> None:
network = Network.testnet()
client = AsyncClient(network)
market_ids = [
"0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"0x7a57e705bb4e09c88aecfc295569481dbf2fe1d5efe364651fbe72385938e9b0",
]
task = asyncio.get_event_loop().create_task(
client.listen_spot_orderbook_snapshots(
market_ids=market_ids,
callback=orderbook_event_processor,
on_end_callback=stream_closed_processor,
on_status_callback=stream_error_processor,
)
)
await asyncio.sleep(delay=60)
task.cancel()
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
)
func main() {
network := common.LoadNetwork("devnet-1", "")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketIds := []string{"0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0"}
stream, err := exchangeClient.StreamSpotOrderbookV2(ctx, marketIds)
if err != nil {
panic(err)
}
for {
select {
case <-ctx.Done():
return
default:
res, err := stream.Recv()
if err != nil {
fmt.Println(err)
return
}
fmt.Println(res.MarketId, res.Orderbook, len(res.Orderbook.Sells), len(res.Orderbook.Buys))
}
}
}
Parameter | Type | Description | Required |
---|---|---|---|
market_ids | String Array | List of market IDs for orderbook streaming; empty means all spot markets | Yes |
callback | Function | Function receiving one parameter (a stream event JSON dictionary) to process each new event | Yes |
on_end_callback | Function | Function with the logic to execute when the stream connection is interrupted | No |
on_status_callback | Function | Function receiving one parameter (the exception) with the logic to execute when an exception happens | No |
Response Parameters
Streaming Response Example:
{
"orderbook":{
"buys":[
{
"price":"0.000000000073489",
"quantity":"129000000000000000",
"timestamp":"1702042963690"
},
{
"price":"0.000000000064261",
"quantity":"1292000000000000000",
"timestamp":"1702039612697"
}
],
"sells":[
{
"price":"0.000000000085",
"quantity":"6681507000000000000000",
"timestamp":"1702044411262"
},
{
"price":"0.000000000085768",
"quantity":"581000000000000000",
"timestamp":"1701944786578"
}
],
"sequence":"6916434",
"timestamp":"1702044439698"
},
"operationType":"update",
"timestamp":"1702044441000",
"marketId":"0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe"
}
Parameter | Type | Description |
---|---|---|
orderbook | SpotLimitOrderbookV2 | Orderbook of a Spot Market |
operation_type | String | Order update type (Should be one of: ["insert", "replace", "update", "invalidate"]) |
timestamp | Integer | Operation timestamp in UNIX millis |
market_id | String | ID of the market the orderbook belongs to |
SpotLimitOrderbookV2
Parameter | Type | Description |
---|---|---|
buys | PriceLevel Array | List of price levels for buys |
sells | PriceLevel Array | List of price levels for sells |
sequence | Integer | Sequence number of the orderbook; increments by 1 each update |
PriceLevel
Parameter | Type | Description |
---|---|---|
price | String | Price number of the price level |
quantity | String | Quantity of the price level |
timestamp | Integer | Price level last updated timestamp in UNIX millis |
StreamOrderbookUpdate
Stream incremental orderbook updates for one or more spot markets. This stream should be started prior to obtaining orderbook snapshots so that no incremental updates are omitted between obtaining a snapshot and starting the update stream.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from decimal import Decimal
from typing import Any, Dict
from grpc import RpcError
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
def stream_error_processor(exception: RpcError):
print(f"There was an error listening to spot orderbook updates ({exception})")
def stream_closed_processor():
print("The spot orderbook updates stream has been closed")
class PriceLevel:
def __init__(self, price: Decimal, quantity: Decimal, timestamp: int):
self.price = price
self.quantity = quantity
self.timestamp = timestamp
def __str__(self) -> str:
return "price: {} | quantity: {} | timestamp: {}".format(self.price, self.quantity, self.timestamp)
class Orderbook:
def __init__(self, market_id: str):
self.market_id = market_id
self.sequence = -1
self.levels = {"buys": {}, "sells": {}}
async def load_orderbook_snapshot(async_client: AsyncClient, orderbook: Orderbook):
# load the snapshot
res = await async_client.fetch_spot_orderbooks_v2(market_ids=[orderbook.market_id])
for snapshot in res["orderbooks"]:
if snapshot["marketId"] != orderbook.market_id:
raise Exception("unexpected snapshot")
orderbook.sequence = int(snapshot["orderbook"]["sequence"])
for buy in snapshot["orderbook"]["buys"]:
orderbook.levels["buys"][buy["price"]] = PriceLevel(
price=Decimal(buy["price"]),
quantity=Decimal(buy["quantity"]),
timestamp=int(buy["timestamp"]),
)
for sell in snapshot["orderbook"]["sells"]:
orderbook.levels["sells"][sell["price"]] = PriceLevel(
price=Decimal(sell["price"]),
quantity=Decimal(sell["quantity"]),
timestamp=int(sell["timestamp"]),
)
break
async def main() -> None:
# select network: local, testnet, mainnet
network = Network.testnet()
async_client = AsyncClient(network)
market_id = "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe"
orderbook = Orderbook(market_id=market_id)
updates_queue = asyncio.Queue()
tasks = []
async def queue_event(event: Dict[str, Any]):
await updates_queue.put(event)
# start getting price levels updates
task = asyncio.get_event_loop().create_task(
async_client.listen_spot_orderbook_updates(
market_ids=[market_id],
callback=queue_event,
on_end_callback=stream_closed_processor,
on_status_callback=stream_error_processor,
)
)
tasks.append(task)
# load the snapshot once we are already receiving updates, so we don't miss any
await load_orderbook_snapshot(async_client=async_client, orderbook=orderbook)
task = asyncio.get_event_loop().create_task(
apply_orderbook_update(orderbook=orderbook, updates_queue=updates_queue)
)
tasks.append(task)
await asyncio.sleep(delay=60)
for task in tasks:
task.cancel()
async def apply_orderbook_update(orderbook: Orderbook, updates_queue: asyncio.Queue):
while True:
updates = await updates_queue.get()
update = updates["orderbookLevelUpdates"]
# discard updates older than the snapshot
if int(update["sequence"]) <= orderbook.sequence:
return
print(" * * * * * * * * * * * * * * * * * * *")
# ensure we have not missed any update
if int(update["sequence"]) > (orderbook.sequence + 1):
raise Exception(
"missing orderbook update events from stream, must restart: {} vs {}".format(
update["sequence"], (orderbook.sequence + 1)
)
)
print("updating orderbook with updates at sequence {}".format(update["sequence"]))
# update orderbook
orderbook.sequence = int(update["sequence"])
for direction, levels in {"buys": update["buys"], "sells": update["sells"]}.items():
for level in levels:
if level["isActive"]:
# upsert level
orderbook.levels[direction][level["price"]] = PriceLevel(
price=Decimal(level["price"]), quantity=Decimal(level["quantity"]), timestamp=level["timestamp"]
)
else:
if level["price"] in orderbook.levels[direction]:
del orderbook.levels[direction][level["price"]]
# sort the level numerically
buys = sorted(orderbook.levels["buys"].values(), key=lambda x: x.price, reverse=True)
sells = sorted(orderbook.levels["sells"].values(), key=lambda x: x.price, reverse=True)
# lowest sell price should be higher than the highest buy price
if len(buys) > 0 and len(sells) > 0:
highest_buy = buys[0].price
lowest_sell = sells[-1].price
print("Max buy: {} - Min sell: {}".format(highest_buy, lowest_sell))
if highest_buy >= lowest_sell:
raise Exception("crossed orderbook, must restart")
# for the example, print the list of buys and sells orders.
print("sells")
for k in sells:
print(k)
print("=========")
print("buys")
for k in buys:
print(k)
print("====================================")
if __name__ == "__main__":
asyncio.run(main())
package main
import (
"context"
"fmt"
"io"
"os"
"sort"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
spotExchangePB "github.com/InjectiveLabs/sdk-go/exchange/spot_exchange_rpc/pb"
"github.com/shopspring/decimal"
)
type MapOrderbook struct {
Sequence uint64
Levels map[bool]map[string]*spotExchangePB.PriceLevel
}
func main() {
network := common.LoadNetwork("devnet-1", "")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
fmt.Println(err)
panic(err)
}
ctx := context.Background()
marketIds := []string{"0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0"}
stream, err := exchangeClient.StreamSpotOrderbookUpdate(ctx, marketIds)
if err != nil {
fmt.Println(err)
panic(err)
}
updatesCh := make(chan *spotExchangePB.OrderbookLevelUpdates, 100000)
receiving := make(chan struct{})
var receivingClosed bool
// stream orderbook price levels
go func() {
for {
select {
case <-ctx.Done():
return
default:
res, err := stream.Recv()
if err == io.EOF {
fmt.Println("change stream needs to be restarted")
os.Exit(0)
}
if err != nil {
panic(err) // rester on errors
}
u := res.OrderbookLevelUpdates
if !receivingClosed {
fmt.Println("receiving updates from stream")
close(receiving)
receivingClosed = true
}
updatesCh <- u
}
}
}()
// ensure we are receiving updates before getting the orderbook snapshot,
// we will skip past updates later.
fmt.Println("waiting for streaming updates")
<-receiving
// prepare orderbooks map
orderbooks := map[string]*MapOrderbook{}
res, err := exchangeClient.GetSpotOrderbooksV2(ctx, marketIds)
if err != nil {
panic(err)
}
for _, ob := range res.Orderbooks {
// init inner maps not ready
_, ok := orderbooks[ob.MarketId]
if !ok {
orderbook := &MapOrderbook{
Sequence: ob.Orderbook.Sequence,
Levels: map[bool]map[string]*spotExchangePB.PriceLevel{},
}
orderbook.Levels[true] = map[string]*spotExchangePB.PriceLevel{}
orderbook.Levels[false] = map[string]*spotExchangePB.PriceLevel{}
orderbooks[ob.MarketId] = orderbook
}
for _, level := range ob.Orderbook.Buys {
orderbooks[ob.MarketId].Levels[true][level.Price] = level
}
for _, level := range ob.Orderbook.Sells {
orderbooks[ob.MarketId].Levels[false][level.Price] = level
}
}
// continuously consume level updates and maintain orderbook
skippedPastEvents := false
for {
updates := <-updatesCh
// validate orderbook
orderbook, ok := orderbooks[updates.MarketId]
if !ok {
fmt.Println("updates channel closed, must restart")
return // closed
}
// skip if update sequence <= orderbook sequence until it's ready to consume
if !skippedPastEvents {
if orderbook.Sequence >= updates.Sequence {
continue
}
skippedPastEvents = true
}
// panic if update sequence > orderbook sequence + 1
if updates.Sequence > orderbook.Sequence+1 {
fmt.Printf("skipping levels: update sequence %d vs orderbook sequence %d\n", updates.Sequence, orderbook.Sequence)
panic("missing orderbook update events from stream, must restart")
}
// update orderbook map
orderbook.Sequence = updates.Sequence
for isBuy, update := range map[bool][]*spotExchangePB.PriceLevelUpdate{
true: updates.Buys,
false: updates.Sells,
} {
for _, level := range update {
if level.IsActive {
// upsert
orderbook.Levels[isBuy][level.Price] = &spotExchangePB.PriceLevel{
Price: level.Price,
Quantity: level.Quantity,
Timestamp: level.Timestamp,
}
} else {
// remove inactive level
delete(orderbook.Levels[isBuy], level.Price)
}
}
}
// construct orderbook arrays
sells, buys := maintainOrderbook(orderbook.Levels)
fmt.Println("after", orderbook.Sequence, len(sells), len(buys))
if len(sells) > 0 && len(buys) > 0 {
// assert orderbook
topBuyPrice := decimal.RequireFromString(buys[0].Price)
lowestSellPrice := decimal.RequireFromString(sells[0].Price)
if topBuyPrice.GreaterThanOrEqual(lowestSellPrice) {
panic(fmt.Errorf("crossed orderbook, must restart: buy %s >= %s sell", topBuyPrice, lowestSellPrice))
}
}
res, _ = exchangeClient.GetSpotOrderbooksV2(ctx, marketIds)
fmt.Println("query", res.Orderbooks[0].Orderbook.Sequence, len(res.Orderbooks[0].Orderbook.Sells), len(res.Orderbooks[0].Orderbook.Buys))
// print orderbook
fmt.Println(" [SELLS] ========")
for _, s := range sells {
fmt.Println(s)
}
fmt.Println(" [BUYS] ========")
for _, b := range buys {
fmt.Println(b)
}
fmt.Println("=======================================================")
}
}
func maintainOrderbook(orderbook map[bool]map[string]*spotExchangePB.PriceLevel) (buys, sells []*spotExchangePB.PriceLevel) {
for _, v := range orderbook[false] {
sells = append(sells, v)
}
for _, v := range orderbook[true] {
buys = append(buys, v)
}
sort.Slice(sells, func(i, j int) bool {
return decimal.RequireFromString(sells[i].Price).LessThan(decimal.RequireFromString(sells[j].Price))
})
sort.Slice(buys, func(i, j int) bool {
return decimal.RequireFromString(buys[i].Price).GreaterThan(decimal.RequireFromString(buys[j].Price))
})
return sells, buys
}
Parameter | Type | Description | Required |
---|---|---|---|
market_ids | String Array | List of market IDs for orderbook streaming; empty means all spot markets | Yes |
callback | Function | Function receiving one parameter (a stream event JSON dictionary) to process each new event | Yes |
on_end_callback | Function | Function with the logic to execute when the stream connection is interrupted | No |
on_status_callback | Function | Function receiving one parameter (the exception) with the logic to execute when an exception happens | No |
Response Parameters
Streaming Response Example:
* * * * * * * * * * * * * * * * * * *
updating orderbook with updates at sequence 724
Max buy: 7.523E-12 - Min sell: 7.525E-12
sells
price: 8E-12 | quantity: 10000000000000000 | timestamp: 1675904636889
price: 7.526E-12 | quantity: 50000000000000000 | timestamp: 1676089482358
price: 7.525E-12 | quantity: 10000000000000000 | timestamp: 1676015247335
=========
buys
price: 7.523E-12 | quantity: 30000000000000000 | timestamp: 1676616192052
price: 7E-12 | quantity: 1000000000000000000 | timestamp: 1675904400063
price: 1E-12 | quantity: 10000000000000000 | timestamp: 1675882430039
price: 1E-15 | quantity: 17983000000000000000 | timestamp: 1675880932648
====================================
* * * * * * * * * * * * * * * * * * *
updating orderbook with updates at sequence 725
Max buy: 7.523E-12 - Min sell: 7.525E-12
sells
price: 8E-12 | quantity: 10000000000000000 | timestamp: 1675904636889
price: 7.526E-12 | quantity: 50000000000000000 | timestamp: 1676089482358
price: 7.525E-12 | quantity: 10000000000000000 | timestamp: 1676015247335
=========
buys
price: 7.523E-12 | quantity: 40000000000000000 | timestamp: 1676616222476
price: 7E-12 | quantity: 1000000000000000000 | timestamp: 1675904400063
price: 1E-12 | quantity: 10000000000000000 | timestamp: 1675882430039
price: 1E-15 | quantity: 17983000000000000000 | timestamp: 1675880932648
====================================
Parameter | Type | Description |
---|---|---|
orderbook_level_updates | OrderbookLevelUpdates | Orderbook level updates of a spot market |
operation_type | String | Order update type (Should be one of: ["insert", "replace", "update", "invalidate"]) |
timestamp | Integer | Operation timestamp in UNIX millis |
market_id | String | ID of the market the orderbook belongs to |
OrderbookLevelUpdates
Parameter | Type | Description |
---|---|---|
market_id | String | ID of the market the orderbook belongs to |
sequence | Integer | Orderbook update sequence number; increments by 1 each update |
buys | PriceLevelUpdate Array | List of buy level updates |
sells | PriceLevelUpdate Array | List of sell level updates |
updated_at | Integer | Timestamp of the updates in UNIX millis |
PriceLevelUpdate
Parameter | Type | Description |
---|---|---|
price | String | Price number of the price level |
quantity | String | Quantity of the price level |
is_active | Boolean | Price level status |
timestamp | Integer | Price level last updated timestamp in UNIX millis |
SubaccountOrdersList
Get orders of a subaccount.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.client.model.pagination import PaginationOption
from pyinjective.core.network import Network
async def main() -> None:
# select network: local, testnet, mainnet
network = Network.testnet()
client = AsyncClient(network)
subaccount_id = "0xc7dca7c15c364865f77a4fb67ab11dc95502e6fe000000000000000000000001"
market_id = "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe"
skip = 10
limit = 10
pagination = PaginationOption(skip=skip, limit=limit)
orders = await client.fetch_spot_subaccount_orders_list(
subaccount_id=subaccount_id, market_id=market_id, pagination=pagination
)
print(orders)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
spotExchangePB "github.com/InjectiveLabs/sdk-go/exchange/spot_exchange_rpc/pb"
)
func main() {
// network := common.LoadNetwork("mainnet", "k8s")
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketId := "0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0"
subaccountId := "0x0b46e339708ea4d87bd2fcc61614e109ac374bbe000000000000000000000000"
skip := uint64(0)
limit := int32(10)
req := spotExchangePB.SubaccountOrdersListRequest{
MarketId: marketId,
SubaccountId: subaccountId,
Skip: skip,
Limit: limit,
}
res, err := exchangeClient.GetSubaccountSpotOrdersList(ctx, &req)
if err != nil {
fmt.Println(err)
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Parameter | Type | Description | Required |
---|---|---|---|
subaccount_id | String | Filter by subaccount ID | Yes |
market_id | String | Filter by market ID | No |
pagination | PaginationOption | Pagination configuration | No |
Response Parameters
Response Example:
{
"orders":[
{
"orderHash":"0x3414f6f1a37a864166b19930680eb62a99798b5e406c2d14ed1ee66ab9958503",
"orderSide":"buy",
"marketId":"0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"subaccountId":"0xbdaedec95d563fb05240d6e01821008454c24c36000000000000000000000000",
"price":"0.000000000003",
"quantity":"55000000000000000000",
"unfilledQuantity":"55000000000000000000",
"triggerPrice":"0",
"feeRecipient":"inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"state":"booked",
"createdAt":"1701808096494",
"updatedAt":"1701808096494",
"txHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"cid":"670c52ec-f68f-456c-8aeb-e271071a43ac"
},
{
"orderHash":"0xb7b6d54d1e01e1eb0005e34e08a96b715b557eeee7c5f3a439636f98ddd66b91",
"orderSide":"buy",
"marketId":"0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"subaccountId":"0xbdaedec95d563fb05240d6e01821008454c24c36000000000000000000000000",
"price":"0.000000000003",
"quantity":"55000000000000000000",
"unfilledQuantity":"55000000000000000000",
"triggerPrice":"0",
"feeRecipient":"inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"state":"booked",
"createdAt":"1701808058512",
"updatedAt":"1701808058512",
"txHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"cid":"bba97476-e7f4-4313-874b-7ef115daccb4"
}
],
"paging":{
"total":"53",
"from":1,
"to":2,
"countBySubaccount":"0",
"next":[
]
}
}
{
"orders": [
{
"order_hash": "0x5e970df47eb5a65a5f907e3a2912067dde416eca8609c838e08c0dbebfbefaa5",
"order_side": "sell",
"market_id": "0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"price": "0.000000000005",
"quantity": "1000000000000000000",
"unfilled_quantity": "1000000000000000000",
"trigger_price": "0",
"fee_recipient": "inj1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8dkncm8",
"state": "booked",
"created_at": 1652809317404,
"updated_at": 1652809317404
},
{
"order_hash": "0x318418b546563a75c11dc656ee0fb41608e2893b0de859cf2b9e2d65996b6f9c",
"order_side": "buy",
"market_id": "0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"price": "0.000000000001",
"quantity": "1000000000000000000",
"unfilled_quantity": "1000000000000000000",
"trigger_price": "0",
"fee_recipient": "inj1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8dkncm8",
"state": "booked",
"created_at": 1652809253308,
"updated_at": 1652809253308
}
]
}
Parameter | Type | Description |
---|---|---|
orders | SpotLimitOrder Array | List of spot market orders |
paging | Paging | Pagination of results |
SpotLimitOrder
Parameter | Type | Description |
---|---|---|
order_hash | String | Hash of the order |
order_side | String | The side of the order (Should be one of: ["buy", "sell", "stop_buy", "stop_sell", "take_buy", "take_sell"]) |
market_id | String | ID of the market the order belongs to |
subaccount_id | String | The subaccount ID the order belongs to |
price | String | The price of the order |
quantity | String | The quantity of the order |
unfilled_quantity | String | The amount of the quantity remaining unfilled |
trigger_price | String | The price that triggers stop and take orders. If no price is set, the default is 0 |
fee_recipient | String | The address that receives fees if the order is executed |
state | String | State of the order (Should be one of: ["booked", "partial_filled", "filled", "canceled"]) |
created_at | Integer | Order committed timestamp in UNIX millis |
updated_at | Integer | Order updated timestamp in UNIX millis |
tx_hash | String | Transaction hash in which the order was created (not all orders have this value) |
cid | String | Identifier for the order specified by the user (up to 36 characters, like a UUID) |
Paging
Parameter | Type | Description |
---|---|---|
total | Integer | Total number of available records |
from | Integer | Lower bound of indices of records returned |
to | integer | Upper bound of indices of records returned |
SubaccountTradesList
Get trades of a subaccount.
IP rate limit group: indexer
*Trade execution types
"market"
for market orders"limitFill"
for a resting limit order getting filled by a market order"limitMatchRestingOrder"
for a resting limit order getting matched with another new limit order"limitMatchNewOrder"
for a new limit order getting matched immediately
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.client.model.pagination import PaginationOption
from pyinjective.core.network import Network
async def main() -> None:
# select network: local, testnet, mainnet
network = Network.testnet()
client = AsyncClient(network)
subaccount_id = "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000"
market_id = "0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe"
execution_type = "market"
direction = "buy"
skip = 2
limit = 3
pagination = PaginationOption(skip=skip, limit=limit)
trades = await client.fetch_spot_subaccount_trades_list(
subaccount_id=subaccount_id,
market_id=market_id,
execution_type=execution_type,
direction=direction,
pagination=pagination,
)
print(trades)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
spotExchangePB "github.com/InjectiveLabs/sdk-go/exchange/spot_exchange_rpc/pb"
)
func main() {
// network := common.LoadNetwork("mainnet", "k8s")
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketId := "0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0"
subaccountId := "0xc6fe5d33615a1c52c08018c47e8bc53646a0e101000000000000000000000000"
skip := uint64(0)
limit := int32(10)
req := spotExchangePB.SubaccountTradesListRequest{
MarketId: marketId,
SubaccountId: subaccountId,
Skip: skip,
Limit: limit,
}
res, err := exchangeClient.GetSubaccountSpotTradesList(ctx, &req)
if err != nil {
fmt.Println(err)
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Parameter | Type | Description | Required |
---|---|---|---|
subaccount_id | String | Filter by subaccount ID | Yes |
market_id | String | Filter by market ID | No |
execution_type | String | Filter by the *execution type of the trades (Should be one of: ["market", "limitFill", "limitMatchRestingOrder", "limitMatchNewOrder"]) | No |
direction | String | Filter by the direction of the trades (Should be one of: ["buy", "sell"]) | No |
pagination | PaginationOption | Pagination configuration | No |
Response Parameters
Response Example:
{
"trades":[
{
"orderHash":"0x6dfd01151a5b3cfb3a61777335f0d8d324a479b9006fd9642f52b402aec53d4f",
"subaccountId":"0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"marketId":"0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"tradeExecutionType":"market",
"tradeDirection":"buy",
"price":{
"price":"0.000000000015589",
"quantity":"1000000000000000",
"timestamp":"1700675201676"
},
"fee":"10.9123",
"executedAt":"1700675201676",
"feeRecipient":"inj1zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3t5qxqh",
"tradeId":"18740619_240_0",
"executionSide":"taker",
"cid":""
},
{
"orderHash":"0x6a6cd0afb038322b67a88cd827e2800483e3571c5e82663df37a33770fa0a8d1",
"subaccountId":"0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"marketId":"0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"tradeExecutionType":"market",
"tradeDirection":"buy",
"price":{
"price":"0.000000000015742",
"quantity":"1000000000000000",
"timestamp":"1700232025894"
},
"fee":"11.0194",
"executedAt":"1700232025894",
"feeRecipient":"inj1zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3t5qxqh",
"tradeId":"18529043_240_0",
"executionSide":"taker",
"cid":""
},
{
"orderHash":"0xa3049ebaa97ac3755c09bd53ea30e86b98aef40bf037cb9d91dedfc1fd4b7735",
"subaccountId":"0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"marketId":"0x0611780ba69656949525013d947713300f56c37b6175e02f26bffa495c3208fe",
"tradeExecutionType":"limitMatchNewOrder",
"tradeDirection":"buy",
"price":{
"price":"0.000000000015874",
"quantity":"21000000000000000000",
"timestamp":"1700221121919"
},
"fee":"233347.8",
"executedAt":"1700221121919",
"feeRecipient":"inj1jv65s3grqf6v6jl3dp4t6c9t9rk99cd8dkncm8",
"tradeId":"18523837_243_0",
"executionSide":"taker",
"cid":""
}
]
}
{
"trades": [
{
"order_hash": "0xbf5cf18a5e73c61d465a60ca550c5fbe0ed37b9ca0a49f7bd1de012e983fe55e",
"subaccount_id": "0xc6fe5d33615a1c52c08018c47e8bc53646a0e101000000000000000000000000",
"market_id": "0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0",
"trade_execution_type": "limitFill",
"trade_direction": "sell",
"price": {
"price": "0.000000000002305",
"quantity": "1000000000000000000",
"timestamp": 1652809734211
},
"fee": "2305",
"executed_at": 1652809734211,
"fee_recipient": "inj1cml96vmptgw99syqrrz8az79xer2pcgp0a885r"
},
{
"order_hash": "0xfd474dc696dc291bca8ca1b371653994fd846a303c08d26ccc17a7b60939d256",
"subaccount_id": "0xc6fe5d33615a1c52c08018c47e8bc53646a0e101000000000000000000000000",
"market_id": "0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0",
"trade_execution_type": "limitFill",
"trade_direction": "sell",
"price": {
"price": "0.000000000002318",
"quantity": "4000000000000000000",
"timestamp": 1652773190338
},
"fee": "9272",
"executed_at": 1652773190338,
"fee_recipient": "inj1cml96vmptgw99syqrrz8az79xer2pcgp0a885r"
}
]
}
Parameter | Type | Description |
---|---|---|
trades | SpotTrade Array | List of spot market trades |
SpotTrade
Parameter | Type | Description |
---|---|---|
order_hash | String | The order hash |
subaccount_id | String | The subaccountId that executed the trade |
market_id | String | The ID of the market that this trade is in |
trade_execution_type | String | Execution type of the trade (Should be one of: ["market", "limitFill", "limitMatchRestingOrder", "limitMatchNewOrder"]) |
trade_direction | String | Direction of the trade(Should be one of: ["buy", "sell"]) |
price | PriceLevel | Price level at which trade has been executed |
fee | String | The fee associated with the trade (quote asset denom) |
executed_at | Integer | Timestamp of trade execution (on chain) in UNIX millis |
fee_recipient | String | The address that received 40% of the fees |
trade_id | String | Unique identifier to differentiate between trades |
execution_side | String | Execution side of trade (Should be one of: ["maker", "taker"]) |
cid | String | Identifier for the order specified by the user (up to 36 characters, like a UUID) |
PriceLevel
Parameter | Type | Description |
---|---|---|
price | String | Price number of the price level |
quantity | String | Quantity of the price level |
timestamp | Integer | Price level last updated timestamp in UNIX millis |
- InjectiveDerivativeExchangeRPC
InjectiveDerivativeExchangeRPC defines the gRPC API of the Derivative Exchange provider.
Market
Get details of a single derivative market.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def main() -> None:
# select network: local, testnet, mainnet
network = Network.testnet()
client = AsyncClient(network)
market_id = "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6"
market = await client.fetch_derivative_market(market_id=market_id)
print(market)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
)
func main() {
// network := common.LoadNetwork("mainnet", "k8s")
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketId := "0x4ca0f92fc28be0c9761326016b5a1a2177dd6375558365116b5bdda9abc229ce"
res, err := exchangeClient.GetDerivativeMarket(ctx, marketId)
if err != nil {
panic(err)
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Parameter | Type | Description | Required |
---|---|---|---|
market_id | String | ID of the market to fetch | Yes |
Response Parameters
Response Example:
{
"market":{
"marketId":"0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"marketStatus":"active",
"ticker":"INJ/USDT PERP",
"oracleBase":"0x2d9315a88f3019f8efa88dfe9c0f0843712da0bac814461e27733f6b83eb51b3",
"oracleQuote":"0x1fc18861232290221461220bd4e2acd1dcdfbc89c84092c93c18bdc7756c1588",
"oracleType":"pyth",
"oracleScaleFactor":6,
"initialMarginRatio":"0.05",
"maintenanceMarginRatio":"0.02",
"quoteDenom":"peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5",
"quoteTokenMeta":{
"name":"Testnet Tether USDT",
"address":"0x0000000000000000000000000000000000000000",
"symbol":"USDT",
"logo":"https://static.alchemyapi.io/images/assets/825.png",
"decimals":6,
"updatedAt":"1698247516758"
},
"makerFeeRate":"-0.0001",
"takerFeeRate":"0.001",
"serviceProviderFee":"0.4",
"isPerpetual":true,
"minPriceTickSize":"100",
"minQuantityTickSize":"0.0001",
"perpetualMarketInfo":{
"hourlyFundingRateCap":"0.000625",
"hourlyInterestRate":"0.00000416666",
"nextFundingTimestamp":"1701990000",
"fundingInterval":"3600"
},
"perpetualMarketFunding":{
"cumulativeFunding":"-156010.283874921534910863",
"cumulativePrice":"566.477789213654772072",
"lastTimestamp":"1701906508"
},
"min_notional":"1000000"
}
}
{
"market": {
"market_id": "0x4ca0f92fc28be0c9761326016b5a1a2177dd6375558365116b5bdda9abc229ce",
"market_status": "active",
"ticker": "BTC/USDT PERP",
"oracle_base": "BTC",
"oracle_quote": "USDT",
"oracle_type": "bandibc",
"oracle_scale_factor": 6,
"initial_margin_ratio": "0.095",
"maintenance_margin_ratio": "0.05",
"quote_denom": "peggy0xdAC17F958D2ee523a2206206994597C13D831ec7",
"quote_token_meta": {
"name": "Tether",
"address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"symbol": "USDT",
"logo": "https://static.alchemyapi.io/images/assets/825.png",
"decimals": 6,
"updated_at": 1650978923435
},
"maker_fee_rate": "0.0005",
"taker_fee_rate": "0.0012",
"service_provider_fee": "0.4",
"is_perpetual": true,
"min_price_tick_size": "100000",
"min_quantity_tick_size": "0.0001",
"perpetual_market_info": {
"hourly_funding_rate_cap": "0.000625",
"hourly_interest_rate": "0.00000416666",
"next_funding_timestamp": 1652864400,
"funding_interval": 3600
},
"perpetual_market_funding": {
"cumulative_funding": "7246105747.050586213851272386",
"cumulative_price": "31.114148427047982579",
"last_timestamp": 1652793510
},
"min_notional": "1000000"
}
}
Parameter | Type | Description |
---|---|---|
market | DerivativeMarketInfo | Info about a particular derivative market |
DerivativeMarketInfo
Parameter | Type | Description |
---|---|---|
oracle_quote | String | Oracle quote currency |
oracle_type | String | Oracle Type |
quote_denom | String | Coin denom used for the quote asset |
is_perpetual | Boolean | True if the market is a perpetual swap market |
maker_fee_rate | String | Defines the fee percentage makers pay (or receive, if negative) in quote asset when trading |
min_price_tick_size | String | Defines the minimum required tick size for the order's price |
min_quantity_tick_size | String | Defines the minimum required tick size for the order's quantity |
oracle_scale_factor | Integer | Scaling multiple to scale oracle prices to the correct number of decimals |
taker_fee_rate | String | Defines the fee percentage takers pay (in quote asset) when trading |
expiry_futures_market_info | ExpiryFuturesMarketInfo | Info about expiry futures market |
initial_margin_ratio | String | The initial margin ratio of the derivative market |
market_status | String | The status of the market (Should be one of: ["active", "paused", "suspended", "demolished", "expired"]) |
service_provider_fee | String | Percentage of the transaction fee shared with the service provider |
oracle_base | String | Oracle base currency |
perpetual_market_funding | PerpetualMarketFunding | PerpetualMarketFunding object |
perpetual_market_info | PerpetualMarketInfo | Information about the perpetual market |
ticker | String | The name of the pair in format AAA/BBB, where AAA is the base asset and BBB is the quote asset |
maintenance_margin_ratio | String | The maintenance margin ratio of the derivative market |
market_id | String | The market ID |
quoteTokenMeta | TokenMeta | Token metadata for quote asset, only for Ethereum-based assets |
min_notional | String | Defines the minimum required notional for an order to be accepted |
ExpiryFuturesMarketInfo
Parameter | Type | Description |
---|---|---|
expiration_timestamp | Integer | Defines the expiration time for a time expiry futures market in UNIX seconds |
settlement_price | String | Defines the settlement price for a time expiry futures market |
PerpetualMarketFunding
Parameter | Type | Description |
---|---|---|
cumulative_funding | String | Defines the cumulative funding of a perpetual market |
cumulative_price | String | Defines the cumulative price for the current hour up to the last timestamp |
last_timestamp | Integer | Defines the last funding timestamp in UNIX seconds |
PerpetualMarketInfo
Parameter | Type | Description |
---|---|---|
hourly_funding_rate_cap | String | Defines the default maximum absolute value of the hourly funding rate |
hourly_interest_rate | String | Defines the hourly interest rate of the perpetual market |
next_funding_timestamp | Integer | Defines the next funding timestamp in UNIX seconds |
funding_interval | Integer | Defines the funding interval in seconds |
TokenMeta
Parameter | Type | Description |
---|---|---|
address | String | Token's Ethereum contract address |
decimals | Integer | Token decimals |
logo | String | URL to the logo image |
name | String | Token full name |
symbol | String | Token symbol short name |
updatedAt | Integer | Token metadata fetched timestamp in UNIX millis |
Markets
Get a list of one or more derivative markets.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def main() -> None:
# select network: local, testnet, mainnet
network = Network.testnet()
client = AsyncClient(network)
market_statuses = ["active"]
quote_denom = "peggy0x87aB3B4C8661e07D6372361211B96ed4Dc36B1B5"
market = await client.fetch_derivative_markets(market_statuses=market_statuses, quote_denom=quote_denom)
print(market)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
derivativeExchangePB "github.com/InjectiveLabs/sdk-go/exchange/derivative_exchange_rpc/pb"
)
func main() {
// network := common.LoadNetwork("mainnet", "k8s")
network := common.LoadNetwork("mainnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketStatus := "active"
quoteDenom := "peggy0xdAC17F958D2ee523a2206206994597C13D831ec7"
req := derivativeExchangePB.MarketsRequest{
MarketStatus: marketStatus,
QuoteDenom: quoteDenom,
}
res, err := exchangeClient.GetDerivativeMarkets(ctx, &req)
if err != nil {
panic(err)
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Parameter | Type | Description | Required |
---|---|---|---|
market_statuses | String Array | Filter by market status (Should be any of: ["active", "paused", "suspended", "demolished", "expired"]) | No |
quote_denom | String | Filter by the Coin denomination of the quote currency | No |
Response Parameters
Response Example:
[
{
"marketId": "0x1c79dac019f73e4060494ab1b4fcba734350656d6fc4d474f6a238c13c6f9ced",
"marketStatus": "active",
"ticker": "BNB/USDT PERP",
"oracleBase": "BNB",
"oracleQuote": "USDT",
"oracleType": "bandibc",
"oracleScaleFactor": 6,
"initialMarginRatio": "0.095",
"maintenanceMarginRatio": "0.05",
"quoteDenom": "peggy0xdAC17F958D2ee523a2206206994597C13D831ec7",
"quoteTokenMeta": {
"name": "Tether",
"address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"symbol": "USDT",
"logo": "https://static.alchemyapi.io/images/assets/825.png",
"decimals": 6,
"updatedAt": 1650978923353
},
"makerFeeRate": "0.0005",
"takerFeeRate": "0.0012",
"serviceProviderFee": "0.4",
"isPerpetual": true,
"minPriceTickSize": "10000",
"minQuantityTickSize": "0.01",
"perpetualMarketInfo": {
"hourlyFundingRateCap": "0.000625",
"hourlyInterestRate": "0.00000416666",
"nextFundingTimestamp": 1654246800,
"fundingInterval": 3600
},
"perpetualMarketFunding": {
"cumulativeFunding": "56890491.178246679699729639",
"cumulativePrice": "7.082760891515203314",
"lastTimestamp": 1654245985
},
"min_notional": "1000000",
},
{
"marketId": "0x00030df39180df04a873cb4aadc50d4135640af5c858ab637dbd4d31b147478c",
"marketStatus": "active",
"ticker": "Frontrunner Futures: Expires 5.21.2023",
"oracleBase": "FRNT",
"oracleQuote": "USDT",
"oracleType": "pricefeed",
"oracleScaleFactor": 6,
"initialMarginRatio": "0.999999999999999999",
"maintenanceMarginRatio": "0.1",
"quoteDenom": "peggy0xdAC17F958D2ee523a2206206994597C13D831ec7",
"quoteTokenMeta": {
"name": "Tether",
"address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"symbol": "USDT",
"logo": "https://static.alchemyapi.io/images/assets/825.png",
"decimals": 6,
"updatedAt": 1653064108501
},
"makerFeeRate": "0.005",
"takerFeeRate": "0.012",
"serviceProviderFee": "0.4",
"isPerpetual": false,
"minPriceTickSize": "0.000000000000001",
"minQuantityTickSize": "0.0001",
"expiryFuturesMarketInfo": {
"expirationTimestamp": 1684600043,
"settlementPrice": "0"
},
"min_notional": "0"
}
]
{
"markets": [
{
"market_id": "0x1c79dac019f73e4060494ab1b4fcba734350656d6fc4d474f6a238c13c6f9ced",
"market_status": "active",
"ticker": "BNB/USDT PERP",
"oracle_base": "BNB",
"oracle_quote": "USDT",
"oracle_type": "bandibc",
"oracle_scale_factor": 6,
"initial_margin_ratio": "0.095",
"maintenance_margin_ratio": "0.05",
"quote_denom": "peggy0xdAC17F958D2ee523a2206206994597C13D831ec7",
"quote_token_meta": {
"name": "Tether",
"address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"symbol": "USDT",
"logo": "https://static.alchemyapi.io/images/assets/825.png",
"decimals": 6,
"updated_at": 1650978923353
},
"maker_fee_rate": "0.0005",
"taker_fee_rate": "0.0012",
"service_provider_fee": "0.4",
"is_perpetual": true,
"min_price_tick_size": "10000",
"min_quantity_tick_size": "0.01",
"perpetual_market_info": {
"hourly_funding_rate_cap": "0.000625",
"hourly_interest_rate": "0.00000416666",
"next_funding_timestamp": 1652864400,
"funding_interval": 3600
},
"perpetual_market_funding": {
"cumulative_funding": "48248742.484852568471323698",
"cumulative_price": "5.691379282523162906",
"last_timestamp": 1652775374
},
"min_notional": "1000000"
},
{
"market_id": "0xfb5f14852bd01af901291dd2aa65e997b3a831f957124a7fe7aa40d218ff71ae",
"market_status": "active",
"ticker": "XAG/USDT PERP",
"oracle_base": "XAG",
"oracle_quote": "USDT",
"oracle_type": "bandibc",
"oracle_scale_factor": 6,
"initial_margin_ratio": "0.8",
"maintenance_margin_ratio": "0.4",
"quote_denom": "peggy0xdAC17F958D2ee523a2206206994597C13D831ec7",
"quote_token_meta": {
"name": "Tether",
"address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"symbol": "USDT",
"logo": "https://static.alchemyapi.io/images/assets/825.png",
"decimals": 6,
"updated_at": 1650978923534
},
"maker_fee_rate": "0.003",
"taker_fee_rate": "0.005",
"service_provider_fee": "0.4",
"is_perpetual": true,
"min_price_tick_size": "10000",
"min_quantity_tick_size": "0.01",
"perpetual_market_info": {
"hourly_funding_rate_cap": "0.000625",
"hourly_interest_rate": "0.00000416666",
"next_funding_timestamp": 1652864400,
"funding_interval": 3600
},
"perpetual_market_funding": {
"cumulative_funding": "1099659.417190990913058692",
"cumulative_price": "-4.427475055338306767",
"last_timestamp": 1652775322
},
"min_notional": "0",
}
]
}
Parameter | Type | Description |
---|---|---|
markets | DerivativeMarketInfo Array | List of derivative markets and associated info |
DerivativeMarketInfo
Parameter | Type | Description |
---|---|---|
oracle_quote | String | Oracle quote currency |
oracle_type | String | Oracle Type |
quote_denom | String | Coin denom used for the quote asset |
is_perpetual | Boolean | True if the market is a perpetual swap market |
maker_fee_rate | String | Defines the fee percentage makers pay (or receive, if negative) in quote asset when trading |
min_price_tick_size | String | Defines the minimum required tick size for the order's price |
min_quantity_tick_size | String | Defines the minimum required tick size for the order's quantity |
oracle_scale_factor | Integer | Scaling multiple to scale oracle prices to the correct number of decimals |
taker_fee_rate | String | Defines the fee percentage takers pay (in quote asset) when trading |
expiry_futures_market_info | ExpiryFuturesMarketInfo | Info about expiry futures market |
initial_margin_ratio | String | The initial margin ratio of the derivative market |
market_status | String | The status of the market (Should be one of: ["active", "paused", "suspended", "demolished", "expired"]) |
service_provider_fee | String | Percentage of the transaction fee shared with the service provider |
oracle_base | String | Oracle base currency |
perpetual_market_funding | PerpetualMarketFunding | PerpetualMarketFunding object |
perpetual_market_info | PerpetualMarketInfo | Information about the perpetual market |
ticker | String | The name of the pair in format AAA/BBB, where AAA is the base asset and BBB is the quote asset |
maintenance_margin_ratio | String | The maintenance margin ratio of the derivative market |
market_id | String | The market ID |
quoteTokenMeta | TokenMeta | Token metadata for quote asset, only for Ethereum-based assets |
min_notional | String | Defines the minimum required notional for an order to be accepted |
ExpiryFuturesMarketInfo
Parameter | Type | Description |
---|---|---|
expiration_timestamp | Integer | Defines the expiration time for a time expiry futures market in UNIX seconds |
settlement_price | String | Defines the settlement price for a time expiry futures market |
PerpetualMarketFunding
Parameter | Type | Description |
---|---|---|
cumulative_funding | String | Defines the cumulative funding of a perpetual market |
cumulative_price | String | Defines the cumulative price for the current hour up to the last timestamp |
last_timestamp | Integer | Defines the last funding timestamp in UNIX seconds |
PerpetualMarketInfo
Parameter | Type | Description |
---|---|---|
hourly_funding_rate_cap | String | Defines the default maximum absolute value of the hourly funding rate |
hourly_interest_rate | String | Defines the hourly interest rate of the perpetual market |
next_funding_timestamp | Integer | Defines the next funding timestamp in UNIX seconds |
funding_interval | Integer | Defines the funding interval in seconds |
TokenMeta
Parameter | Type | Description |
---|---|---|
address | String | Token's Ethereum contract address |
decimals | Integer | Token decimals |
logo | String | URL to the logo image |
name | String | Token full name |
symbol | String | Token symbol short name |
updatedAt | Integer | Token metadata fetched timestamp in UNIX millis |
StreamMarkets
Stream live updates of derivative markets.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from typing import Any, Dict
from grpc import RpcError
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def market_event_processor(event: Dict[str, Any]):
print(event)
def stream_error_processor(exception: RpcError):
print(f"There was an error listening to derivative markets updates ({exception})")
def stream_closed_processor():
print("The derivative markets updates stream has been closed")
async def main() -> None:
# select network: local, testnet, mainnet
network = Network.testnet()
client = AsyncClient(network)
task = asyncio.get_event_loop().create_task(
client.listen_derivative_market_updates(
callback=market_event_processor,
on_end_callback=stream_closed_processor,
on_status_callback=stream_error_processor,
)
)
await asyncio.sleep(delay=60)
task.cancel()
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
)
func main() {
// network := common.LoadNetwork("mainnet", "k8s")
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketIds := []string{"0x4ca0f92fc28be0c9761326016b5a1a2177dd6375558365116b5bdda9abc229ce"}
stream, err := exchangeClient.StreamDerivativeMarket(ctx, marketIds)
if err != nil {
panic(err)
}
for {
select {
case <-ctx.Done():
return
default:
res, err := stream.Recv()
if err != nil {
panic(err)
return
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
}
}
Parameter | Type | Description | Required |
---|---|---|---|
market_ids | String Array | List of market IDs for updates streaming, empty means 'ALL' derivative markets | No |
callback | Function | Function receiving one parameter (a stream event JSON dictionary) to process each new event | Yes |
on_end_callback | Function | Function with the logic to execute when the stream connection is interrupted | No |
on_status_callback | Function | Function receiving one parameter (the exception) with the logic to execute when an exception happens | No |
Response Parameters
Streaming Response Example:
{
"market": {
"marketId": "0x4ca0f92fc28be0c9761326016b5a1a2177dd6375558365116b5bdda9abc229ce",
"marketStatus": "active",
"ticker": "BTC/USDT PERP",
"oracleBase": "BTC",
"oracleQuote": "USDT",
"oracleType": "bandibc",
"oracleScaleFactor": 6,
"initialMarginRatio": "0.095",
"maintenanceMarginRatio": "0.05",
"quoteDenom": "peggy0xdAC17F958D2ee523a2206206994597C13D831ec7",
"quoteTokenMeta": {
"name": "Tether",
"address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"symbol": "USDT",
"logo": "https://static.alchemyapi.io/images/assets/825.png",
"decimals": 6,
"updatedAt": 1650978923435
},
"makerFeeRate": "0.0005",
"takerFeeRate": "0.0012",
"serviceProviderFee": "0.4",
"isPerpetual": true,
"minPriceTickSize": "100000",
"minQuantityTickSize": "0.0001",
"perpetualMarketInfo": {
"hourlyFundingRateCap": "0.000625",
"hourlyInterestRate": "0.00000416666",
"nextFundingTimestamp": 1654246800,
"fundingInterval": 3600
},
"perpetualMarketFunding": {
"cumulativeFunding": "8239865636.851083559033030036",
"cumulativePrice": "7.15770685160786651",
"lastTimestamp": 1654246073
},
"min_notional": "1000000"
},
"operationType": "update",
"timestamp": 1654246076000
}
{
"market": {
"market_id": "0x4ca0f92fc28be0c9761326016b5a1a2177dd6375558365116b5bdda9abc229ce",
"market_status": "active",
"ticker": "BTC/USDT PERP",
"oracle_base": "BTC",
"oracle_quote": "USDT",
"oracle_type": "bandibc",
"oracle_scale_factor": 6,
"initial_margin_ratio": "0.095",
"maintenance_margin_ratio": "0.05",
"quote_denom": "peggy0xdAC17F958D2ee523a2206206994597C13D831ec7",
"quote_token_meta": {
"name": "Tether",
"address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"symbol": "USDT",
"logo": "https://static.alchemyapi.io/images/assets/825.png",
"decimals": 6,
"updated_at": 1650978923435
},
"maker_fee_rate": "0.0005",
"taker_fee_rate": "0.0012",
"service_provider_fee": "0.4",
"is_perpetual": true,
"min_price_tick_size": "100000",
"min_quantity_tick_size": "0.0001",
"perpetual_market_info": {
"hourly_funding_rate_cap": "0.000625",
"hourly_interest_rate": "0.00000416666",
"next_funding_timestamp": 1653040800,
"funding_interval": 3600
},
"perpetual_market_funding": {
"cumulative_funding": "7356035675.459202347630388315",
"cumulative_price": "3.723976370878870887",
"last_timestamp": 1653038971
},
"min_notional": "0"
},
"operation_type": "update",
"timestamp": 1653038974000
}
Parameter | Type | Description |
---|---|---|
market | DerivativeMarketInfo | Info about a particular derivative market |
operation_type | String | Update type (Should be one of: ["insert", "delete", "replace", "update", "invalidate"]) |
timestamp | Integer | Operation timestamp in UNIX millis |
DerivativeMarketInfo
Parameter | Type | Description |
---|---|---|
oracle_quote | String | Oracle quote currency |
oracle_type | String | Oracle Type |
quote_denom | String | Coin denom used for the quote asset |
is_perpetual | Boolean | True if the market is a perpetual swap market |
maker_fee_rate | String | Defines the fee percentage makers pay (or receive, if negative) in quote asset when trading |
min_price_tick_size | String | Defines the minimum required tick size for the order's price |
min_quantity_tick_size | String | Defines the minimum required tick size for the order's quantity |
oracle_scale_factor | Integer | Scaling multiple to scale oracle prices to the correct number of decimals |
taker_fee_rate | String | Defines the fee percentage takers pay (in quote asset) when trading |
expiry_futures_market_info | ExpiryFuturesMarketInfo | Info about expiry futures market |
initial_margin_ratio | String | The initial margin ratio of the derivative market |
market_status | String | The status of the market (Should be one of: ["active", "paused", "suspended", "demolished", "expired"]) |
service_provider_fee | String | Percentage of the transaction fee shared with the service provider |
oracle_base | String | Oracle base currency |
perpetual_market_funding | PerpetualMarketFunding | PerpetualMarketFunding object |
perpetual_market_info | PerpetualMarketInfo | Information about the perpetual market |
ticker | String | The name of the pair in format AAA/BBB, where AAA is the base asset and BBB is the quote asset |
maintenance_margin_ratio | String | The maintenance margin ratio of the derivative market |
market_id | String | The market ID |
quoteTokenMeta | TokenMeta | Token metadata for quote asset, only for Ethereum-based assets |
min_notional | String | Defines the minimum required notional for an order to be accepted |
ExpiryFuturesMarketInfo
Parameter | Type | Description |
---|---|---|
expiration_timestamp | Integer | Defines the expiration time for a time expiry futures market in UNIX seconds |
settlement_price | String | Defines the settlement price for a time expiry futures market |
PerpetualMarketFunding
Parameter | Type | Description |
---|---|---|
cumulative_funding | String | Defines the cumulative funding of a perpetual market |
cumulative_price | String | Defines the cumulative price for the current hour up to the last timestamp |
last_timestamp | Integer | Defines the last funding timestamp in UNIX seconds |
PerpetualMarketInfo
Parameter | Type | Description |
---|---|---|
hourly_funding_rate_cap | String | Defines the default maximum absolute value of the hourly funding rate |
hourly_interest_rate | String | Defines the hourly interest rate of the perpetual market |
next_funding_timestamp | Integer | Defines the next funding timestamp in UNIX seconds |
funding_interval | Integer | Defines the funding interval in seconds |
TokenMeta
Parameter | Type | Description |
---|---|---|
address | String | Token's Ethereum contract address |
decimals | Integer | Token decimals |
logo | String | URL to the logo image |
name | String | Token full name |
symbol | String | Token symbol short name |
updatedAt | Integer | Token metadata fetched timestamp in UNIX millis |
OrdersHistory
Lists historical orders posted from a subaccount
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.client.model.pagination import PaginationOption
from pyinjective.core.network import Network
async def main() -> None:
# select network: local, testnet, mainnet
network = Network.testnet()
client = AsyncClient(network)
market_ids = ["0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6"]
subaccount_id = "0x295639d56c987f0e24d21bb167872b3542a6e05a000000000000000000000000"
is_conditional = "false"
skip = 10
limit = 3
pagination = PaginationOption(skip=skip, limit=limit)
orders = await client.fetch_derivative_orders_history(
subaccount_id=subaccount_id,
market_ids=market_ids,
is_conditional=is_conditional,
pagination=pagination,
)
print(orders)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
derivativeExchangePB "github.com/InjectiveLabs/sdk-go/exchange/derivative_exchange_rpc/pb"
)
func main() {
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketId := "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6"
subaccountId := "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000"
skip := uint64(0)
limit := int32(10)
isConditional := "false"
req := derivativeExchangePB.OrdersHistoryRequest{
SubaccountId: subaccountId,
MarketId: marketId,
Skip: skip,
Limit: limit,
IsConditional: isConditional,
}
res, err := exchangeClient.GetHistoricalDerivativeOrders(ctx, &req)
if err != nil {
panic(err)
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Parameter | Type | Description | Required |
---|---|---|---|
subaccount_id | String | Filter by subaccount ID | No |
market_ids | String Array | Filter by multiple market IDs | No |
order_types | String Array | The order types to be included (Should be any of: ["buy", "sell", "stop_buy", "stop_sell", "take_buy", "take_sell", "buy_po", "sell_po"]) | No |
direction | String | Filter by order direction (Should be one of: ["buy", "sell"]) | No |
is_conditional | String | Search for conditional/non-conditional orders(Should be one of: ["true", "false"]) | No |
state | String | The order state (Should be one of: ["booked", "partial_filled", "filled", "canceled"]) | No |
execution_types | String Array | The execution of the order (Should be one of: ["limit", "market"]) | No |
trade_id | String | Filter by the trade's trade id | No |
active_markets_only | Bool | Return only orders for active markets | No |
cid | String | Filter by the custom client order id of the trade's order | No |
pagination | PaginationOption | Pagination configuration | No |
Response Parameters
Response Example:
{
"orders":[
{
"orderHash":"0x3c98f1e38781ce8780f8f5ffe3a78cf3dbfa4d96f54d774a4847f46e9e89d6dc",
"marketId":"0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccountId":"0x5e249f0e8cb406f41de16e1bd6f6b55e7bc75add000000000000000000000005",
"executionType":"limit",
"orderType":"buy_po",
"price":"13097700",
"triggerPrice":"0",
"quantity":"110660.5284",
"filledQuantity":"0",
"state":"canceled",
"createdAt":"1701995288222",
"updatedAt":"1702002966962",
"direction":"buy",
"margin":"724699201412.34",
"txHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"isActive":false,
"isReduceOnly":false,
"isConditional":false,
"triggerAt":"0",
"placedOrderHash":"",
"cid":""
},
{
"orderHash":"0xc7c34746158b1e21bbdfa8edce0876fa00810f7b6f2236c302945c8e6b9eeb38",
"marketId":"0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccountId":"0x5e249f0e8cb406f41de16e1bd6f6b55e7bc75add000000000000000000000005",
"executionType":"limit",
"orderType":"buy_po",
"price":"13064100",
"triggerPrice":"0",
"quantity":"110945.1399",
"filledQuantity":"0",
"state":"canceled",
"createdAt":"1701995288222",
"updatedAt":"1702002966962",
"direction":"buy",
"margin":"724699201083.795",
"txHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"isActive":false,
"isReduceOnly":false,
"isConditional":false,
"triggerAt":"0",
"placedOrderHash":"",
"cid":""
},
{
"orderHash":"0x4478897d21c94df929c7d12c33d71973027ced1899ff253199b6e46124e680c1",
"marketId":"0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccountId":"0x5e249f0e8cb406f41de16e1bd6f6b55e7bc75add000000000000000000000005",
"executionType":"limit",
"orderType":"buy_po",
"price":"13030500",
"triggerPrice":"0",
"quantity":"111231.2193",
"filledQuantity":"0",
"state":"canceled",
"createdAt":"1701995288222",
"updatedAt":"1702002966962",
"direction":"buy",
"margin":"724699201544.325",
"txHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"isActive":false,
"isReduceOnly":false,
"isConditional":false,
"triggerAt":"0",
"placedOrderHash":"",
"cid":""
}
],
"paging":{
"total":"1000",
"from":0,
"to":0,
"countBySubaccount":"0",
"next":[
]
}
}
{
"orders": [
{
"order_hash": "0x17da6aa0ba9c192da6c9d5a043b4c36a91af5117636cb1f6e69654fc6cfc1bee",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"execution_type": "market",
"order_type": "sell",
"price": "6494113.703056872037914692",
"trigger_price": "0",
"quantity": "2110",
"filled_quantity": "2110",
"state": "filled",
"created_at": 1692857306725,
"updated_at": 1692857306725,
"direction": "sell",
"margin": "0"
},
{
"order_hash": "0xeac94983c5e1a47885e5959af073c475e4ec6ec343c1e1d441af1ba65f8aa5ee",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"execution_type": "limit",
"order_type": "buy",
"price": "8111100",
"trigger_price": "0",
"quantity": "10",
"filled_quantity": "10",
"state": "filled",
"created_at": 1688738648747,
"updated_at": 1688738648747,
"direction": "buy",
"margin": "82614000"
},
{
"order_hash": "0x41a5c6f8c8c8ff3f37e443617dda589f46f1678ef1a22e2ab2b6ca54e0788210",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"execution_type": "market",
"order_type": "buy",
"price": "8261400",
"trigger_price": "0",
"quantity": "100",
"filled_quantity": "100",
"state": "filled",
"created_at": 1688591280030,
"updated_at": 1688591280030,
"direction": "buy",
"margin": "274917218.543"
},
{
"order_hash": "0x2f667629cd06ac9fd6e54aa2544ad63cd43efc29418d0e1e12df9ba783c9a7ab",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"execution_type": "market",
"order_type": "buy",
"price": "7166668.98546",
"trigger_price": "0",
"quantity": "2000",
"filled_quantity": "2000",
"state": "filled",
"created_at": 1687507605674,
"updated_at": 1687507605674,
"direction": "buy",
"margin": "4814400000"
},
{
"order_hash": "0x4c42bca7b65f18bf96e75be03a53f73854da15e8030c38e63d1531307f8cd40c",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"execution_type": "market",
"order_type": "sell",
"price": "7123287.02926",
"trigger_price": "0",
"quantity": "1000",
"filled_quantity": "1000",
"state": "filled",
"created_at": 1687507547684,
"updated_at": 1687507547684,
"direction": "sell",
"margin": "0"
},
{
"order_hash": "0x70c66ce3e92aa616d3dbdbcd565c37a3d42b2b5a0951a8bebe9ddaca9852d547",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"execution_type": "market",
"order_type": "buy",
"price": "7162504.91532",
"trigger_price": "0",
"quantity": "1000",
"filled_quantity": "1000",
"state": "filled",
"created_at": 1687507348068,
"updated_at": 1687507348068,
"direction": "buy",
"margin": "7212200000"
}
],
"paging": {
"total": 6
}
}
Parameter | Type | Description |
---|---|---|
orders | DerivativeOrderHistory Array | list of historical derivative orders |
paging | Paging | Pagination of results |
DerivativeOrderHistory
Parameter | Type | Description |
---|---|---|
order_hash | String | Hash of the order |
market_id | String | Derivative market ID |
is_active | Boolean | Indicates if the order is active |
subaccount_id | String | The subaccountId that this order belongs to |
execution_type | String | The type of the order (Should be one of: ["limit", "market"]) |
order_type | String | Order type (Should be one of: ["buy", "sell", "stop_buy", "stop_sell", "take_buy", "take_sell", "buy_po", "sell_po"]) |
price | String | Price of the order |
trigger_price | String | The price that triggers stop/take orders |
quantity | String | Quantity of the order |
filled_quantity | String | The amount of the quantity filled |
state | String | Order state (Should be one of: ["booked", "partial_filled", "filled", "canceled"]) |
created_at | Integer | Order created timestamp in UNIX millis |
updated_at | Integer | Order updated timestamp in UNIX millis |
is_reduce_only | Boolean | Indicates if the order is reduce-only |
direction | String | The direction of the order (Should be one of: ["buy", "sell"]) |
is_conditional | Boolean | Indicates if the order is conditional |
trigger_at | Integer | Trigger timestamp in UNIX millis |
placed_order_hash | String | Hash of order placed upon conditional order trigger |
margin | String | The margin of the order |
tx_hash | String | Transaction hash in which the order was created (not all orders have this value) |
cid | String | Identifier for the order specified by the user (up to 36 characters, like a UUID) |
Paging
Parameter | Type | Description |
---|---|---|
total | Integer | Total number of available records |
StreamOrdersHistory
Stream order updates of a derivative market.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from typing import Any, Dict
from grpc import RpcError
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def order_event_processor(event: Dict[str, Any]):
print(event)
def stream_error_processor(exception: RpcError):
print(f"There was an error listening to derivative orders history updates ({exception})")
def stream_closed_processor():
print("The derivative orders history updates stream has been closed")
async def main() -> None:
# select network: local, testnet, mainnet
network = Network.testnet()
client = AsyncClient(network)
market_id = "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6"
task = asyncio.get_event_loop().create_task(
client.listen_derivative_orders_history_updates(
callback=order_event_processor,
on_end_callback=stream_closed_processor,
on_status_callback=stream_error_processor,
market_id=market_id,
)
)
await asyncio.sleep(delay=60)
task.cancel()
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
derivativeExchangePB "github.com/InjectiveLabs/sdk-go/exchange/derivative_exchange_rpc/pb"
)
func main() {
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketId := "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6"
subaccountId := "0xc6fe5d33615a1c52c08018c47e8bc53646a0e101000000000000000000000000"
direction := "buy"
req := derivativeExchangePB.StreamOrdersHistoryRequest{
MarketId: marketId,
SubaccountId: subaccountId,
Direction: direction,
}
stream, err := exchangeClient.StreamHistoricalDerivativeOrders(ctx, &req)
if err != nil {
panic(err)
}
for {
select {
case <-ctx.Done():
return
default:
res, err := stream.Recv()
if err != nil {
panic(err)
return
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
}
}
Parameter | Type | Description | Required |
---|---|---|---|
subaccount_id | String | Filter by subaccount ID | No |
market_id | String | Filter by market ID | No |
order_types | String Array | Filter by order type (Should be one of: ["buy", "sell", "stop_buy", "stop_sell", "take_buy", "take_sell", "buy_po", "sell_po"]) | No |
direction | String | Filter by direction (Should be one of: ["buy", "sell"]) | No |
state | String | Filter by state (Should be one of: ["booked", "partial_filled", "filled", "canceled"]) | No |
execution_types | String Array | Filter by execution type (Should be one of: ["limit", "market"]) | No |
callback | Function | Function receiving one parameter (a stream event JSON dictionary) to process each new event | Yes |
on_end_callback | Function | Function with the logic to execute when the stream connection is interrupted | No |
on_status_callback | Function | Function receiving one parameter (the exception) with the logic to execute when an exception happens | No |
Response Parameters
Streaming Response Example:
{
"order":{
"order_hash":"0x4aeb72ac2ae5811126a0c384e05ce68745316add0e705c39e73f68c76431515e",
"market_id":"0x141e3c92ed55107067ceb60ee412b86256cedef67b1227d6367b4cdf30c55a74",
"is_active":true,
"subaccount_id":"0x7619f89a2172c6705aac7482f3adbf0601ea140e000000000000000000000000",
"execution_type":"limit",
"order_type":"sell_po",
"price":"27953000000",
"trigger_price":"0",
"quantity":"0.0344",
"filled_quantity":"0",
"state":"booked",
"created_at":1696617269292,
"updated_at":1696617269292,
"direction":"sell",
"margin":"320527734"
},
"operation_type":"insert",
"timestamp":1696617272000
}
{
"order":{
"order_hash":"0x24d82da3530ce5d2d392c9563d29b79c3a25e058dd6d79e0d8f651703256eb78",
"market_id":"0x141e3c92ed55107067ceb60ee412b86256cedef67b1227d6367b4cdf30c55a74",
"subaccount_id":"0x6590d14d9e9c1d964f8c83bddc8a092f4a2d1284000000000000000000000000",
"execution_type":"limit",
"order_type":"buy_po",
"price":"27912000000",
"trigger_price":"0",
"quantity":"0.0344",
"filled_quantity":"0",
"state":"canceled",
"created_at":1696617207873,
"updated_at":1696617269292,
"direction":"buy",
"margin":"320057600"
},
"operation_type":"update",
"timestamp":1696617272000
}
{
"order":{
"order_hash":"0x4aeb72ac2ae5811126a0c384e05ce68745316add0e705c39e73f68c76431515e",
"market_id":"0x141e3c92ed55107067ceb60ee412b86256cedef67b1227d6367b4cdf30c55a74",
"is_active":true,
"subaccount_id":"0x7619f89a2172c6705aac7482f3adbf0601ea140e000000000000000000000000",
"execution_type":"limit",
"order_type":"sell_po",
"price":"27953000000",
"trigger_price":"0",
"quantity":"0.0344",
"filled_quantity":"0",
"state":"booked",
"created_at":1696617269292,
"updated_at":1696617269292,
"direction":"sell",
"margin":"320527734"
},
"operation_type":"insert",
"timestamp":1696617272000
}
{
"order":{
"order_hash":"0x24d82da3530ce5d2d392c9563d29b79c3a25e058dd6d79e0d8f651703256eb78",
"market_id":"0x141e3c92ed55107067ceb60ee412b86256cedef67b1227d6367b4cdf30c55a74",
"subaccount_id":"0x6590d14d9e9c1d964f8c83bddc8a092f4a2d1284000000000000000000000000",
"execution_type":"limit",
"order_type":"buy_po",
"price":"27912000000",
"trigger_price":"0",
"quantity":"0.0344",
"filled_quantity":"0",
"state":"canceled",
"created_at":1696617207873,
"updated_at":1696617269292,
"direction":"buy",
"margin":"320057600"
},
"operation_type":"update",
"timestamp":1696617272000
}
Parameter | Type | Description |
---|---|---|
order | DerivativeOrderHistory | Updated order |
operation_type | String | Order update type (Should be one of: ["insert", "replace", "update", "invalidate"]) |
timestamp | Integer | Operation timestamp in UNIX millis |
DerivativeOrderHistory
Parameter | Type | Description |
---|---|---|
order_hash | String | Hash of the order |
market_id | String | Derivative market ID |
is_active | Boolean | Indicates if the order is active |
subaccount_id | String | The subaccountId that this order belongs to |
execution_type | String | The type of the order (Should be one of: ["limit", "market"]) |
order_type | String | Order type (Should be one of: ["buy", "sell", "stop_buy", "stop_sell", "take_buy", "take_sell", "buy_po", "sell_po"]) |
price | String | Price of the order |
trigger_price | String | The price that triggers stop/take orders |
quantity | String | Quantity of the order |
filled_quantity | String | The amount of the quantity filled |
state | String | Order state (Should be one of: ["booked", "partial_filled", "filled", "canceled"]) |
created_at | Integer | Order created timestamp in UNIX millis |
updated_at | Integer | Order updated timestamp in UNIX millis |
is_reduce_only | Boolean | Indicates if the order is reduce-only |
direction | String | The direction of the order (Should be one of: ["buy", "sell"]) |
is_conditional | Boolean | Indicates if the order is conditional |
trigger_at | Integer | Trigger timestamp in UNIX millis |
placed_order_hash | String | Hash of order placed upon conditional order trigger |
margin | String | The margin of the order |
tx_hash | String | Transaction hash in which the order was created (not all orders have this value) |
cid | String | Identifier for the order specified by the user (up to 36 characters, like a UUID) |
TradesV2
Get trades of a derivative market.
The difference between Trades
and TradesV2
is that the latter returns a trade_id
compatible witht the one used for trade events in chain stream.
IP rate limit group: indexer
*Trade execution types
"market"
for market orders"limitFill"
for a resting limit order getting filled by a market order"limitMatchRestingOrder"
for a resting limit order getting matched with another new limit order"limitMatchNewOrder"
for a new limit order getting matched immediately
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.client.model.pagination import PaginationOption
from pyinjective.core.network import Network
async def main() -> None:
# select network: local, testnet, mainnet
network = Network.testnet()
client = AsyncClient(network)
market_ids = ["0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6"]
subaccount_ids = ["0xc6fe5d33615a1c52c08018c47e8bc53646a0e101000000000000000000000000"]
skip = 0
limit = 4
pagination = PaginationOption(skip=skip, limit=limit)
trades = await client.fetch_derivative_trades(
market_ids=market_ids, subaccount_ids=subaccount_ids, pagination=pagination
)
print(trades)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
derivativeExchangePB "github.com/InjectiveLabs/sdk-go/exchange/derivative_exchange_rpc/pb"
)
func main() {
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketId := "0x4ca0f92fc28be0c9761326016b5a1a2177dd6375558365116b5bdda9abc229ce"
subaccountId := "0xc6fe5d33615a1c52c08018c47e8bc53646a0e101000000000000000000000000"
req := derivativeExchangePB.TradesV2Request{
MarketId: marketId,
SubaccountId: subaccountId,
}
res, err := exchangeClient.GetDerivativeTradesV2(ctx, &req)
if err != nil {
panic(err)
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Parameter | Type | Description | Required |
---|---|---|---|
market_ids | String Array | Filter by multiple market IDs | No |
subaccount_ids | String Array | Filter by multiple subaccount IDs | No |
execution_side | String | Filter by the execution side of the trade (Should be one of: ["maker", "taker"]) | No |
direction | String | Filter by the direction of the trade (Should be one of: ["buy", "sell"]) | No |
execution_types | String Array | Filter by the *trade execution type (Should be any of: ["market", "limitFill", "limitMatchRestingOrder", "limitMatchNewOrder"]) | No |
trade_id | String | Filter by the trade id of the trade | No |
account_address | String | Filter by the account address | No |
cid | String | Filter by the custom client order id of the trade's order | No |
pagination | PaginationOption | Pagination configuration | No |
Response Parameters
Response Example:
{
"trades":[
{
"orderHash":"0xc246b6a43d826667047f752a76e508511d0aa4f73aba1c3b95527037ccbcb50c",
"subaccountId":"0x101411266c6e2b610b4a0324d2bfb2ef0ca6e1dd000000000000000000000000",
"marketId":"0x56d0c0293c4415e2d48fc2c8503a56a0c7389247396a2ef9b0a48c01f0646705",
"tradeExecutionType":"limitMatchRestingOrder",
"positionDelta":{
"tradeDirection":"buy",
"executionPrice":"10000000",
"executionQuantity":"1",
"executionMargin":"10000000"
},
"payout":"0",
"fee":"-60000",
"executedAt":"1701978102242",
"feeRecipient":"inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"tradeId":"22868_2",
"executionSide":"maker",
"cid":"49fb387d-aad3-4f03-85c5-e6c06c5ea685",
"isLiquidation":false
},
{
"orderHash":"0x836e778ae11cee6cd31619ca7329121419471be7ea1bd2fafbae3a8d411a04c7",
"subaccountId":"0x101411266c6e2b610b4a0324d2bfb2ef0ca6e1dd000000000000000000000000",
"marketId":"0x7cc8b10d7deb61e744ef83bdec2bbcf4a056867e89b062c6a453020ca82bd4e4",
"tradeExecutionType":"limitMatchRestingOrder",
"positionDelta":{
"tradeDirection":"buy",
"executionPrice":"10000000",
"executionQuantity":"1",
"executionMargin":"10000000"
},
"payout":"0",
"fee":"-600",
"executedAt":"1701978102242",
"feeRecipient":"inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"tradeId":"22868_4",
"executionSide":"maker",
"cid":"9a74f3ce-ea31-491e-9e64-e48541a8f7fd",
"isLiquidation":false
},
{
"orderHash":"0x404577bc40896028733f7740d4efc6f6695fb9194f43dd545fb8923cbb01efd6",
"subaccountId":"0x3db1f84431dfe4df617f9eb2d04edf432beb9826000000000000000000000000",
"marketId":"0x56d0c0293c4415e2d48fc2c8503a56a0c7389247396a2ef9b0a48c01f0646705",
"tradeExecutionType":"limitMatchNewOrder",
"positionDelta":{
"tradeDirection":"sell",
"executionPrice":"10000000",
"executionQuantity":"1",
"executionMargin":"10000000"
},
"payout":"0",
"fee":"5000000",
"executedAt":"1701978102242",
"feeRecipient":"inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"tradeId":"22868_3",
"executionSide":"taker",
"cid":"derivative_ATOM/USDT",
"isLiquidation":false
},
{
"orderHash":"0x3fde93ceabc67a13372c237f5271784c0bbe97801ef12e883cfbde4c13e16300",
"subaccountId":"0x3db1f84431dfe4df617f9eb2d04edf432beb9826000000000000000000000000",
"marketId":"0x7cc8b10d7deb61e744ef83bdec2bbcf4a056867e89b062c6a453020ca82bd4e4",
"tradeExecutionType":"limitMatchNewOrder",
"positionDelta":{
"tradeDirection":"sell",
"executionPrice":"10000000",
"executionQuantity":"1",
"executionMargin":"9000000"
},
"payout":"0",
"fee":"10000",
"executedAt":"1701978102242",
"feeRecipient":"inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"tradeId":"22868_5",
"executionSide":"taker",
"cid":"derivative_INJ/USDT",
"isLiquidation":false
},
{
"orderHash":"0xc246b6a43d826667047f752a76e508511d0aa4f73aba1c3b95527037ccbcb50c",
"subaccountId":"0x101411266c6e2b610b4a0324d2bfb2ef0ca6e1dd000000000000000000000000",
"marketId":"0x56d0c0293c4415e2d48fc2c8503a56a0c7389247396a2ef9b0a48c01f0646705",
"tradeExecutionType":"limitMatchRestingOrder",
"positionDelta":{
"tradeDirection":"buy",
"executionPrice":"10000000",
"executionQuantity":"1",
"executionMargin":"10000000"
},
"payout":"0",
"fee":"-60000",
"executedAt":"1701961116630",
"feeRecipient":"inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"tradeId":"1321_2",
"executionSide":"maker",
"cid":"49fb387d-aad3-4f03-85c5-e6c06c5ea685",
"isLiquidation":false
},
{
"orderHash":"0x836e778ae11cee6cd31619ca7329121419471be7ea1bd2fafbae3a8d411a04c7",
"subaccountId":"0x101411266c6e2b610b4a0324d2bfb2ef0ca6e1dd000000000000000000000000",
"marketId":"0x7cc8b10d7deb61e744ef83bdec2bbcf4a056867e89b062c6a453020ca82bd4e4",
"tradeExecutionType":"limitMatchRestingOrder",
"positionDelta":{
"tradeDirection":"buy",
"executionPrice":"10000000",
"executionQuantity":"1",
"executionMargin":"10000000"
},
"payout":"0",
"fee":"-600",
"executedAt":"1701961116630",
"feeRecipient":"inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"tradeId":"1321_4",
"executionSide":"maker",
"cid":"9a74f3ce-ea31-491e-9e64-e48541a8f7fd",
"isLiquidation":false
},
{
"orderHash":"0xf6ae77b0a1e267b7f0b8618f3f226c39ba702a09fb3bdb650b0c28197d36d91d",
"subaccountId":"0x3db1f84431dfe4df617f9eb2d04edf432beb9826000000000000000000000000",
"marketId":"0x56d0c0293c4415e2d48fc2c8503a56a0c7389247396a2ef9b0a48c01f0646705",
"tradeExecutionType":"limitMatchNewOrder",
"positionDelta":{
"tradeDirection":"sell",
"executionPrice":"10000000",
"executionQuantity":"1",
"executionMargin":"10000000"
},
"payout":"0",
"fee":"5000000",
"executedAt":"1701961116630",
"feeRecipient":"inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"tradeId":"1321_3",
"executionSide":"taker",
"cid":"49fb387d-aad3-4f03-85c5-e6c06c5ea685",
"isLiquidation":false
},
{
"orderHash":"0x01ba4dd3b30bc946a31f5fe1aba9918e95d3dc8cf31a6c4b2d793068eae529e8",
"subaccountId":"0x3db1f84431dfe4df617f9eb2d04edf432beb9826000000000000000000000000",
"marketId":"0x7cc8b10d7deb61e744ef83bdec2bbcf4a056867e89b062c6a453020ca82bd4e4",
"tradeExecutionType":"limitMatchNewOrder",
"positionDelta":{
"tradeDirection":"sell",
"executionPrice":"10000000",
"executionQuantity":"1",
"executionMargin":"9000000"
},
"payout":"0",
"fee":"10000",
"executedAt":"1701961116630",
"feeRecipient":"inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"tradeId":"1321_5",
"executionSide":"taker",
"cid":"9a74f3ce-ea31-491e-9e64-e48541a8f7fd",
"isLiquidation":false
}
],
"paging":{
"total":"8",
"from":1,
"to":8,
"countBySubaccount":"0",
"next":[
]
}
}
{
"trades": [
{
"order_hash": "0x836e778ae11cee6cd31619ca7329121419471be7ea1bd2fafbae3a8d411a04c7",
"subaccount_id": "0x101411266c6e2b610b4a0324d2bfb2ef0ca6e1dd000000000000000000000000",
"market_id": "0x7cc8b10d7deb61e744ef83bdec2bbcf4a056867e89b062c6a453020ca82bd4e4",
"trade_execution_type": "limitMatchRestingOrder",
"position_delta": {
"trade_direction": "buy",
"execution_price": "10000000",
"execution_quantity": "1",
"execution_margin": "10000000"
},
"payout": "0",
"fee": "-600",
"executed_at": 1701978102242,
"fee_recipient": "inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"trade_id": "22868_4",
"execution_side": "maker",
"cid": "9a74f3ce-ea31-491e-9e64-e48541a8f7fd"
},
{
"order_hash": "0x3fde93ceabc67a13372c237f5271784c0bbe97801ef12e883cfbde4c13e16300",
"subaccount_id": "0x3db1f84431dfe4df617f9eb2d04edf432beb9826000000000000000000000000",
"market_id": "0x7cc8b10d7deb61e744ef83bdec2bbcf4a056867e89b062c6a453020ca82bd4e4",
"trade_execution_type": "limitMatchNewOrder",
"position_delta": {
"trade_direction": "sell",
"execution_price": "10000000",
"execution_quantity": "1",
"execution_margin": "9000000"
},
"payout": "0",
"fee": "10000",
"executed_at": 1701978102242,
"fee_recipient": "inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"trade_id": "22868_5",
"execution_side": "taker",
"cid": "9a74f3ce-ea31-491e-9e64-e48541a8f7fd"
},
{
"order_hash": "0x836e778ae11cee6cd31619ca7329121419471be7ea1bd2fafbae3a8d411a04c7",
"subaccount_id": "0x101411266c6e2b610b4a0324d2bfb2ef0ca6e1dd000000000000000000000000",
"market_id": "0x7cc8b10d7deb61e744ef83bdec2bbcf4a056867e89b062c6a453020ca82bd4e4",
"trade_execution_type": "limitMatchRestingOrder",
"position_delta": {
"trade_direction": "buy",
"execution_price": "10000000",
"execution_quantity": "1",
"execution_margin": "10000000"
},
"payout": "0",
"fee": "-600",
"executed_at": 1701961116630,
"fee_recipient": "inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"trade_id": "1321_4",
"execution_side": "maker",
"cid": "9a74f3ce-ea31-491e-9e64-e48541a8f7fd"
},
{
"order_hash": "0x01ba4dd3b30bc946a31f5fe1aba9918e95d3dc8cf31a6c4b2d793068eae529e8",
"subaccount_id": "0x3db1f84431dfe4df617f9eb2d04edf432beb9826000000000000000000000000",
"market_id": "0x7cc8b10d7deb61e744ef83bdec2bbcf4a056867e89b062c6a453020ca82bd4e4",
"trade_execution_type": "limitMatchNewOrder",
"position_delta": {
"trade_direction": "sell",
"execution_price": "10000000",
"execution_quantity": "1",
"execution_margin": "9000000"
},
"payout": "0",
"fee": "10000",
"executed_at": 1701961116630,
"fee_recipient": "inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"trade_id": "1321_5",
"execution_side": "taker",
"cid": "9a74f3ce-ea31-491e-9e64-e48541a8f7fd"
}
],
"paging": {
"total": 4,
"from": 1,
"to": 4
}
}
Parameter | Type | Description |
---|---|---|
trades | DerivativeTrade Array | List of trades of derivative markets |
paging | Paging | Pagination of results |
DerivativeTrade
Parameter | Type | Description |
---|---|---|
order_hash | String | The order hash |
subaccount_id | String | ID of subaccount that executed the trade |
market_id | String | The market ID |
trade_execution_type | String | *Execution type of the trade (Should be one of: ["market", "limitFill", "limitMatchRestingOrder", "limitMatchNewOrder"]) |
is_liquidation | Boolean | True if the trade is a liquidation |
position_delta | PositionDelta | Position delta from the trade |
payout | String | The payout associated with the trade |
fee | String | The fee associated with the trade |
executed_at | Integer | Timestamp of trade execution (on chain) in UNIX millis |
fee_recipient | String | The address that received 40% of the fees |
trade_id | String | Unique identifier to differentiate between trades |
execution_side | String | Execution side of trade (Should be one of: ["maker", "taker"]) |
cid | String | Custom client order id |
PositionDelta
Parameter | Type | Description |
---|---|---|
execution_price | String | Execution price of the trade |
execution_quantity | String | Execution quantity of the trade |
trade_direction | String | The direction the trade (Should be one of: ["buy", "sell"]) |
execution_margin | String | Execution margin of the trade |
Paging
Parameter | Type | Description |
---|---|---|
total | Integer | Total number of records available |
StreamTradesV2
Stream newly executed trades of a derivative market. The default request streams trades from all derivative markets.
The difference between StreamTrades
and StreamTradesV2
is that the latter returns a trade_id
compatible witht the one used for trade events in chain stream.
IP rate limit group: indexer
*Trade execution types
"market"
for market orders"limitFill"
for a resting limit order getting filled by a market order"limitMatchRestingOrder"
for a resting limit order getting matched with another new limit order"limitMatchNewOrder"
for a new limit order getting matched immediately
Request Parameters
Request Example:
import asyncio
from typing import Any, Dict
from grpc import RpcError
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def market_event_processor(event: Dict[str, Any]):
print(event)
def stream_error_processor(exception: RpcError):
print(f"There was an error listening to derivative trades updates ({exception})")
def stream_closed_processor():
print("The derivative trades updates stream has been closed")
async def main() -> None:
# select network: local, testnet, mainnet
network = Network.testnet()
client = AsyncClient(network)
market_ids = [
"0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"0x70bc8d7feab38b23d5fdfb12b9c3726e400c265edbcbf449b6c80c31d63d3a02",
]
subaccount_ids = ["0xc6fe5d33615a1c52c08018c47e8bc53646a0e101000000000000000000000000"]
task = asyncio.get_event_loop().create_task(
client.listen_derivative_trades_updates(
callback=market_event_processor,
on_end_callback=stream_closed_processor,
on_status_callback=stream_error_processor,
market_ids=market_ids,
subaccount_ids=subaccount_ids,
)
)
await asyncio.sleep(delay=60)
task.cancel()
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
derivativeExchangePB "github.com/InjectiveLabs/sdk-go/exchange/derivative_exchange_rpc/pb"
)
func main() {
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketId := "0x4ca0f92fc28be0c9761326016b5a1a2177dd6375558365116b5bdda9abc229ce"
subaccountId := "0xc6fe5d33615a1c52c08018c47e8bc53646a0e101000000000000000000000000"
req := derivativeExchangePB.StreamTradesV2Request{
MarketId: marketId,
SubaccountId: subaccountId,
}
stream, err := exchangeClient.StreamDerivativeV2Trades(ctx, &req)
if err != nil {
panic(err)
}
for {
select {
case <-ctx.Done():
return
default:
res, err := stream.Recv()
if err != nil {
panic(err)
return
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
}
}
Parameter | Type | Description | Required |
---|---|---|---|
market_ids | String Array | Filter by multiple market IDs | No |
execution_side | String | Filter by the execution side of the trade (Should be one of: ["maker", "taker"]) | No |
direction | String | Filter by the direction of the trade (Should be one of: ["buy", "sell"]) | No |
subaccount_ids | String Array | Filter by multiple subaccount IDs | No |
execution_types | String Array | Filter by the *trade execution type (Should be one of: ["market", "limitFill", "limitMatchRestingOrder", "limitMatchNewOrder"]) | No |
trade_id | String | Filter by the trade's trade id | No |
account_address | String | Filter by the account address | No |
cid | String | Filter by the custom client order id of the trade's order | No |
pagination | PaginationOption | Pagination configuration | No |
callback | Function | Function receiving one parameter (a stream event JSON dictionary) to process each new event | Yes |
on_end_callback | Function | Function with the logic to execute when the stream connection is interrupted | No |
on_status_callback | Function | Function receiving one parameter (the exception) with the logic to execute when an exception happens | No |
Response Parameters
Streaming Response Example:
{
"trade":{
"orderHash":"0x01e6b3df831734c88c84522f9834b3656b3afde6891ac671742dd269be776510",
"subaccountId":"0x3db1f84431dfe4df617f9eb2d04edf432beb9826000000000000000000000000",
"marketId":"0x56d0c0293c4415e2d48fc2c8503a56a0c7389247396a2ef9b0a48c01f0646705",
"tradeExecutionType":"limitMatchNewOrder",
"positionDelta":{
"tradeDirection":"sell",
"executionPrice":"10000000",
"executionQuantity":"1",
"executionMargin":"10000000"
},
"payout":"0",
"fee":"5000000",
"executedAt":"1701979920875",
"feeRecipient":"inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"tradeId":"25116_3",
"executionSide":"taker",
"cid":"derivative_ATOM/USDT",
"isLiquidation":false
},
"operationType":"insert",
"timestamp":"1701979922000"
}
{
"trade":{
"orderHash":"0xc246b6a43d826667047f752a76e508511d0aa4f73aba1c3b95527037ccbcb50c",
"subaccountId":"0x101411266c6e2b610b4a0324d2bfb2ef0ca6e1dd000000000000000000000000",
"marketId":"0x56d0c0293c4415e2d48fc2c8503a56a0c7389247396a2ef9b0a48c01f0646705",
"tradeExecutionType":"limitMatchRestingOrder",
"positionDelta":{
"tradeDirection":"buy",
"executionPrice":"10000000",
"executionQuantity":"1",
"executionMargin":"10000000"
},
"payout":"0",
"fee":"-60000",
"executedAt":"1701979920875",
"feeRecipient":"inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"tradeId":"25116_2",
"executionSide":"maker",
"cid":"49fb387d-aad3-4f03-85c5-e6c06c5ea685",
"isLiquidation":false
},
"operationType":"insert",
"timestamp":"1701979922000"
}
{
"trade":{
"orderHash":"0xa0446c80b66dd6ab75b32e51e4e04929ae92df9fa7a64fe2d21ed9be536bb6d5",
"subaccountId":"0x3db1f84431dfe4df617f9eb2d04edf432beb9826000000000000000000000000",
"marketId":"0x7cc8b10d7deb61e744ef83bdec2bbcf4a056867e89b062c6a453020ca82bd4e4",
"tradeExecutionType":"limitMatchNewOrder",
"positionDelta":{
"tradeDirection":"sell",
"executionPrice":"10000000",
"executionQuantity":"1",
"executionMargin":"9000000"
},
"payout":"0",
"fee":"10000",
"executedAt":"1701979920875",
"feeRecipient":"inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"tradeId":"25116_5",
"executionSide":"taker",
"cid":"derivative_INJ/USDT",
"isLiquidation":false
},
"operationType":"insert",
"timestamp":"1701979922000"
}
{
"trade":{
"orderHash":"0x836e778ae11cee6cd31619ca7329121419471be7ea1bd2fafbae3a8d411a04c7",
"subaccountId":"0x101411266c6e2b610b4a0324d2bfb2ef0ca6e1dd000000000000000000000000",
"marketId":"0x7cc8b10d7deb61e744ef83bdec2bbcf4a056867e89b062c6a453020ca82bd4e4",
"tradeExecutionType":"limitMatchRestingOrder",
"positionDelta":{
"tradeDirection":"buy",
"executionPrice":"10000000",
"executionQuantity":"1",
"executionMargin":"10000000"
},
"payout":"0",
"fee":"-600",
"executedAt":"1701979920875",
"feeRecipient":"inj1hkhdaj2a2clmq5jq6mspsggqs32vynpk228q3r",
"tradeId":"25116_4",
"executionSide":"maker",
"cid":"9a74f3ce-ea31-491e-9e64-e48541a8f7fd",
"isLiquidation":false
},
"operationType":"insert",
"timestamp":"1701979922000"
}
{
"trade": {
"order_hash": "0x0403d2e51d73aa1cb46004b16d76279afece9ad14e3784eb93aa6370de466f81",
"subaccount_id": "0xc6fe5d33615a1c52c08018c47e8bc53646a0e101000000000000000000000000",
"market_id": "0x4ca0f92fc28be0c9761326016b5a1a2177dd6375558365116b5bdda9abc229ce",
"trade_execution_type": "limitMatchRestingOrder",
"position_delta": {
"trade_direction": "sell",
"execution_price": "40249100000",
"execution_quantity": "0.06",
"execution_margin": "2388462000"
},
"payout": "0",
"fee": "1207473",
"executed_at": 1653040243183,
"fee_recipient": "inj1cml96vmptgw99syqrrz8az79xer2pcgp0a885r"
},
"operation_type": "insert",
"timestamp": 1653040246000
}{
"trade": {
"order_hash": "0x728d69975e4057d1801f1a7031d0ccf7242abacbf73320da55abab677efc2a7e",
"subaccount_id": "0xc6fe5d33615a1c52c08018c47e8bc53646a0e101000000000000000000000000",
"market_id": "0x4ca0f92fc28be0c9761326016b5a1a2177dd6375558365116b5bdda9abc229ce",
"trade_execution_type": "limitMatchRestingOrder",
"position_delta": {
"trade_direction": "sell",
"execution_price": "40249100000",
"execution_quantity": "0.02",
"execution_margin": "779300000"
},
"payout": "0",
"fee": "402491",
"executed_at": 1653040243183,
"fee_recipient": "inj1cml96vmptgw99syqrrz8az79xer2pcgp0a885r"
},
"operation_type": "insert",
"timestamp": 1653040246000
}
Parameter | Type | Description |
---|---|---|
trade | DerivativeTrade | New derivative market trade |
operation_type | String | Trade operation type (Should be one of: ["insert", "invalidate"]) |
timestamp | Integer | Timestamp the new trade is written into the database in UNIX millis |
DerivativeTrade
Parameter | Type | Description |
---|---|---|
order_hash | String | The order hash |
subaccount_id | String | ID of subaccount that executed the trade |
market_id | String | The market ID |
trade_execution_type | String | *Execution type of the trade (Should be one of: ["market", "limitFill", "limitMatchRestingOrder", "limitMatchNewOrder"]) |
is_liquidation | Boolean | True if the trade is a liquidation |
position_delta | PositionDelta | Position delta from the trade |
payout | String | The payout associated with the trade |
fee | String | The fee associated with the trade |
executed_at | Integer | Timestamp of trade execution (on chain) in UNIX millis |
fee_recipient | String | The address that received 40% of the fees |
trade_id | String | Unique identifier to differentiate between trades |
execution_side | String | Execution side of trade (Should be one of: ["maker", "taker"]) |
cid | String | Custom client order id |
PositionDelta
Parameter | Type | Description |
---|---|---|
execution_price | String | Execution price of the trade |
execution_quantity | String | Execution quantity of the trade |
trade_direction | String | The direction the trade (Should be one of: ["buy", "sell"]) |
execution_margin | String | Execution margin of the trade |
Positions
Get the positions of a market.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.client.model.pagination import PaginationOption
from pyinjective.core.network import Network
async def main() -> None:
# select network: local, testnet, mainnet
network = Network.testnet()
client = AsyncClient(network)
market_ids = [
"0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"0xd97d0da6f6c11710ef06315971250e4e9aed4b7d4cd02059c9477ec8cf243782",
]
subaccount_id = "0xc6fe5d33615a1c52c08018c47e8bc53646a0e101000000000000000000000000"
direction = "short"
subaccount_total_positions = False
skip = 4
limit = 4
pagination = PaginationOption(skip=skip, limit=limit)
positions = await client.fetch_derivative_positions_v2(
market_ids=market_ids,
subaccount_id=subaccount_id,
direction=direction,
subaccount_total_positions=subaccount_total_positions,
pagination=pagination,
)
print(positions)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
derivativeExchangePB "github.com/InjectiveLabs/sdk-go/exchange/derivative_exchange_rpc/pb"
)
func main() {
// network := common.LoadNetwork("mainnet", "k8s")
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketId := "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6"
subaccountId := "0xc6fe5d33615a1c52c08018c47e8bc53646a0e101000000000000000000000000"
skip := uint64(0)
limit := int32(10)
req := derivativeExchangePB.PositionsV2Request{
MarketId: marketId,
SubaccountId: subaccountId,
Skip: skip,
Limit: limit,
}
res, err := exchangeClient.GetDerivativePositionsV2(ctx, &req)
if err != nil {
panic(err)
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Parameter | Type | Description | Required |
---|---|---|---|
market_ids | String Array | Filter by multiple market IDs | No |
subaccount_id | String | Filter by subaccount ID | No |
direction | String | Filter by direction of position (Should be one of: ["long", "short"]) | No |
subaccount_total_positions | Boolean | Choose to return subaccount total positions (Should be one of: [True, False]) | No |
pagination | PaginationOption | Pagination configuration | No |
Response Parameters
Response Example:
{
"positions":[
{
"ticker":"INJ/USDT PERP",
"marketId":"0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccountId":"0x0000007c60fab7a70c2ae0ebe437f3726b05e7eb000000000000000000000000",
"direction":"short",
"quantity":"0.087829315829932072",
"entryPrice":"26453271.813315285838444221",
"margin":"1156906.224974",
"liquidationPrice":"38848511.946759",
"markPrice":"35561999.99",
"updatedAt":"1703793600379"
},
{
"ticker":"INJ/USDT PERP",
"marketId":"0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccountId":"0x0000040f1111c5c3d2037940658ee770bb37e0a2000000000000000000000000",
"direction":"long",
"quantity":"0.000068500966722584",
"entryPrice":"12293600",
"margin":"440.389918",
"liquidationPrice":"5984327.380009",
"markPrice":"35561999.99",
"updatedAt":"1703793600379"
},
{
"ticker":"INJ/USDT PERP",
"marketId":"0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccountId":"0x00509ed903475672121d6a1fb2c646eef4da6c44000000000000000000000000",
"direction":"short",
"quantity":"0.022151816236313182",
"entryPrice":"15980281.340438795311756833",
"margin":"172782.601001",
"liquidationPrice":"23313932.022168",
"markPrice":"35561999.99",
"updatedAt":"1703793600379"
},
],
"paging":{
"total":"1166",
"from":0,
"to":0,
"countBySubaccount":"0",
"next":[
]
}
}
{
"positions": [
{
"ticker": "INJ/USDT PERP",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0x0000007c60fab7a70c2ae0ebe437f3726b05e7eb000000000000000000000000",
"direction": "short",
"quantity": "0.087829315829932072",
"entry_price": "26453271.813315285838444221",
"margin": "1156893.718782",
"liquidation_price": "38848372.346758",
"mark_price": "35294291.44",
"updated_at": 1703790001819
},
{
"ticker": "INJ/USDT PERP",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0x0000040f1111c5c3d2037940658ee770bb37e0a2000000000000000000000000",
"direction": "long",
"quantity": "0.000068500966722584",
"entry_price": "12293600",
"margin": "440.399672",
"liquidation_price": "5984182.081895",
"mark_price": "35294291.44",
"updated_at": 1703790001819
},
{
"ticker": "INJ/USDT PERP",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0x00509ed903475672121d6a1fb2c646eef4da6c44000000000000000000000000",
"direction": "short",
"quantity": "0.022151816236313182",
"entry_price": "15980281.340438795311756833",
"margin": "172779.44676",
"liquidation_price": "23313792.422186",
"mark_price": "35294291.44",
"updated_at": 1703790001819
},
{
"ticker": "INJ/USDT PERP",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0x00606da8ef76ca9c36616fa576d1c053bb0f7eb2000000000000000000000000",
"direction": "short",
"quantity": "0.041121486263975195",
"entry_price": "15980281.340438795311756842",
"margin": "320624.783065",
"liquidation_price": "23311073.361647",
"mark_price": "35294291.44",
"updated_at": 1703790001819
},
{
"ticker": "INJ/USDT PERP",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0x00cd2929594559000670af009e2f5ef15fefa6cf000000000000000000000000",
"direction": "long",
"quantity": "0.000050211853307914",
"entry_price": "12293600",
"margin": "322.774915",
"liquidation_price": "5985039.457698",
"mark_price": "35294291.44",
"updated_at": 1703790001819
},
{
"ticker": "INJ/USDT PERP",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0x00cf63fab44e827a4fb12142d6e0d8e82099701a000000000000000000000000",
"direction": "long",
"quantity": "0.000058171926973086",
"entry_price": "12293600",
"margin": "374.127106",
"liquidation_price": "5981833.667338",
"mark_price": "35294291.44",
"updated_at": 1703790001819
},
{
"ticker": "INJ/USDT PERP",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0x010d36443f440708892e79dcbb6c1350c4c76662000000000000000000000000",
"direction": "long",
"quantity": "0.00008708028566368",
"entry_price": "12293600",
"margin": "559.983294",
"liquidation_price": "5982596.709155",
"mark_price": "35294291.44",
"updated_at": 1703790001819
},
{
"ticker": "INJ/USDT PERP",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0x0154fc01caf0e4c82b7ea6c9be719c260e940ef3000000000000000000000000",
"direction": "short",
"quantity": "0.005684726095134712",
"entry_price": "19468749.344033524349659551",
"margin": "54688.034814",
"liquidation_price": "28518548.958621",
"mark_price": "35294291.44",
"updated_at": 1703790001819
},
{
"ticker": "INJ/USDT PERP",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0x0198b3d3691bcc1718dcda4277bb27c68fd19a6f000000000000000000000000",
"direction": "short",
"quantity": "1.950155851194068459",
"entry_price": "34111005.753742189227540816",
"margin": "33194528.351493",
"liquidation_price": "50129882.732098",
"mark_price": "35294291.44",
"updated_at": 1703790001819
},
{
"ticker": "INJ/USDT PERP",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0x021a86b9858f6e4150a724d444e94d10cb75c1b1000000000000000000000000",
"direction": "short",
"quantity": "0.077749303393590607",
"entry_price": "15980281.340438795311756851",
"margin": "605813.214778",
"liquidation_price": "23306040.184987",
"mark_price": "35294291.44",
"updated_at": 1703790001819
}
],
"paging": {
"total": 1153
}
}
Parameter | Type | Description |
---|---|---|
positions | DerivativePosition Array | List of derivative positions |
paging | Paging | Pagination of results |
DerivativePosition
Parameter | Type | Description |
---|---|---|
ticker | String | Ticker of the derivative market |
market_id | String | ID of the market the position is in |
subaccount_id | String | The subaccount ID the position belongs to |
direction | String | Direction of the position (Should be one of: ["long", "short"]) |
quantity | String | Quantity of the position |
entry_price | String | Entry price of the position |
margin | String | Margin of the position |
liquidation_price | String | Liquidation price of the position |
mark_price | String | Oracle price of the base asset |
updated_at | Integer | Position updated timestamp in UNIX millis |
Paging
Parameter | Type | Description |
---|---|---|
total | Integer | Total number of available records |
StreamPositions
Stream position updates for a specific market.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from typing import Any, Dict
from grpc import RpcError
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def positions_event_processor(event: Dict[str, Any]):
print(event)
def stream_error_processor(exception: RpcError):
print(f"There was an error listening to derivative positions updates ({exception})")
def stream_closed_processor():
print("The derivative positions updates stream has been closed")
async def main() -> None:
# select network: local, testnet, mainnet
network = Network.testnet()
client = AsyncClient(network)
market_ids = ["0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6"]
subaccount_ids = ["0xea98e3aa091a6676194df40ac089e40ab4604bf9000000000000000000000000"]
task = asyncio.get_event_loop().create_task(
client.listen_derivative_positions_updates(
callback=positions_event_processor,
on_end_callback=stream_closed_processor,
on_status_callback=stream_error_processor,
market_ids=market_ids,
subaccount_ids=subaccount_ids,
)
)
await asyncio.sleep(delay=60)
task.cancel()
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
derivativeExchangePB "github.com/InjectiveLabs/sdk-go/exchange/derivative_exchange_rpc/pb"
)
func main() {
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketId := "0x4ca0f92fc28be0c9761326016b5a1a2177dd6375558365116b5bdda9abc229ce"
subaccountId := "0xc6fe5d33615a1c52c08018c47e8bc53646a0e101000000000000000000000000"
req := derivativeExchangePB.StreamPositionsRequest{
MarketId: marketId,
SubaccountId: subaccountId,
}
stream, err := exchangeClient.StreamDerivativePositions(ctx, &req)
if err != nil {
panic(err)
}
for {
select {
case <-ctx.Done():
return
default:
res, err := stream.Recv()
if err != nil {
panic(err)
return
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
}
}
Parameter | Type | Description | Required |
---|---|---|---|
market_ids | String Array | IDs of the markets to stream position data from | No |
subaccount_ids | String Array | Subaccount IDs of the traders to stream positions from | No |
callback | Function | Function receiving one parameter (a stream event JSON dictionary) to process each new event | Yes |
on_end_callback | Function | Function with the logic to execute when the stream connection is interrupted | No |
on_status_callback | Function | Function receiving one parameter (the exception) with the logic to execute when an exception happens | No |
Response Parameters
Streaming Response Example:
{
"position": {
"ticker": "BTC/USDT PERP",
"marketId": "0x4ca0f92fc28be0c9761326016b5a1a2177dd6375558365116b5bdda9abc229ce",
"subaccountId": "0xc6fe5d33615a1c52c08018c47e8bc53646a0e101000000000000000000000000",
"direction": "short",
"quantity": "1.6321",
"entryPrice": "40555935751.758890674529114982",
"margin": "65283896141.678537523412631302",
"liquidationPrice": "76719878206.648298",
"markPrice": "40128736026.4094317665",
"aggregateReduceOnlyQuantity": "0"
},
"timestamp": 1654246646000
}
{
"position": {
"ticker": "BTC/USDT PERP",
"marketId": "0x4ca0f92fc28be0c9761326016b5a1a2177dd6375558365116b5bdda9abc229ce",
"subaccountId": "0xc6fe5d33615a1c52c08018c47e8bc53646a0e101000000000000000000000000",
"direction": "short",
"quantity": "1.6321",
"entryPrice": "40489113688.030524225816723708",
"margin": "65069810777.851918748331744252",
"liquidationPrice": "76531312698.56045",
"markPrice": "40128736026.4094317665",
"aggregateReduceOnlyQuantity": "0"
},
"timestamp": 1654246687000
}
{
"position": {
"ticker": "BTC/USDT PERP",
"market_id": "0x4ca0f92fc28be0c9761326016b5a1a2177dd6375558365116b5bdda9abc229ce",
"subaccount_id": "0xc6fe5d33615a1c52c08018c47e8bc53646a0e101000000000000000000000000",
"direction": "short",
"quantity": "0.4499",
"entry_price": "40187334829.308997167725462798",
"margin": "17648170480.844939276952101173",
"liquidation_price": "75632579558.528471",
"mark_price": "40128736026.4094317665",
"aggregate_reduce_only_quantity": "0"
},
"timestamp": 1653039418000
}{
"position": {
"ticker": "BTC/USDT PERP",
"market_id": "0x4ca0f92fc28be0c9761326016b5a1a2177dd6375558365116b5bdda9abc229ce",
"subaccount_id": "0xc6fe5d33615a1c52c08018c47e8bc53646a0e101000000000000000000000000",
"direction": "short",
"quantity": "0.4499",
"entry_price": "40415133266.89312760388339505",
"margin": "17780087110.130349528796488556",
"liquidation_price": "76128781140.582706",
"mark_price": "40128736026.4094317665",
"aggregate_reduce_only_quantity": "0"
},
"timestamp": 1653039464000
}{
"position": {
"ticker": "BTC/USDT PERP",
"market_id": "0x4ca0f92fc28be0c9761326016b5a1a2177dd6375558365116b5bdda9abc229ce",
"subaccount_id": "0xc6fe5d33615a1c52c08018c47e8bc53646a0e101000000000000000000000000",
"direction": "short",
"quantity": "0.4499",
"entry_price": "40306914705.252255649316986606",
"margin": "17654816331.908168110936068341",
"liquidation_price": "75760533574.235878",
"mark_price": "40128736026.4094317665",
"aggregate_reduce_only_quantity": "0"
},
"timestamp": 1653039501000
}
Parameter | Type | Description |
---|---|---|
position | DerivativePosition | Updated derivative position |
timestamp | Integer | Timestamp of update in UNIX millis |
DerivativePosition
Parameter | Type | Description |
---|---|---|
direction | String | Direction of the position (Should be one of: ["long", "short"]) |
market_id | String | ID of the market the position is in |
subaccount_id | String | The subaccount ID the position belongs to |
ticker | String | Ticker of the derivative market |
aggregate_reduce_only_quantity | String | Aggregate quantity of the reduce-only orders associated with the position |
entry_price | String | Entry price of the position |
liquidation_price | String | Liquidation price of the position |
margin | String | Margin of the position |
mark_price | String | Oracle price of the base asset |
quantity | String | Quantity of the position |
updated_at | Integer | Position updated timestamp in UNIX millis |
created_at | Integer | Position created timestamp in UNIX millis. Currently not supported (value will be inaccurate). |
LiquidablePositions
Gets all the liquidable positions
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.client.model.pagination import PaginationOption
from pyinjective.core.network import Network
async def main() -> None:
network = Network.testnet()
client = AsyncClient(network)
market_id = "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6"
skip = 10
limit = 3
pagination = PaginationOption(skip=skip, limit=limit)
positions = await client.fetch_derivative_liquidable_positions(
market_id=market_id,
pagination=pagination,
)
print(positions)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
derivativeExchangePB "github.com/InjectiveLabs/sdk-go/exchange/derivative_exchange_rpc/pb"
)
func main() {
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
//marketId := "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6"
skip := uint64(0)
limit := int32(10)
req := derivativeExchangePB.LiquidablePositionsRequest{
//MarketId: marketId,
Skip: skip,
Limit: limit,
}
res, err := exchangeClient.GetDerivativeLiquidablePositions(ctx, &req)
if err != nil {
panic(err)
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Parameter | Type | Description | Required |
---|---|---|---|
market_id | String | ID of the market to query liquidable positions for | Yes |
skip | Integer | Skip the first n cosmwasm contracts. This can be used to fetch all results since the API caps at 100 | No |
limit | Integer | Max number of items to be returned, defaults to 100 | No |
Response Parameters
Streaming Response Example:
{
"positions":[
{
"ticker":"INJ/USDT PERP",
"marketId":"0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccountId":"0x0a5d67f3616a9e7b53c301b508e9384c6321be47000000000000000000000000",
"direction":"short",
"quantity":"0.00966730135521481",
"entryPrice":"15980281.340438795311756819",
"margin":"75611.273514",
"liquidationPrice":"23334925.188149",
"markPrice":"39291123.99",
"aggregateReduceOnlyQuantity":"0",
"updatedAt":"1705525203015",
"createdAt":"-62135596800000"
},
{
"ticker":"INJ/USDT PERP",
"marketId":"0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccountId":"0x0c812012cf492aa422fb888e172fbd6e19df517b000000000000000000000000",
"direction":"short",
"quantity":"0.066327809378915175",
"entryPrice":"16031762.538045163086357667",
"margin":"520412.029703",
"liquidationPrice":"23409630.791347",
"markPrice":"39291123.99",
"aggregateReduceOnlyQuantity":"0",
"updatedAt":"1705525203015",
"createdAt":"-62135596800000"
},
{
"ticker":"INJ/USDT PERP",
"marketId":"0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccountId":"0x0d750ad3404b6f23579706e44111e8dd774fd1fa000000000000000000000000",
"direction":"short",
"quantity":"0.000080602003464734",
"entryPrice":"16031762.53804516308635803",
"margin":"632.408209",
"liquidationPrice":"23409630.592378",
"markPrice":"39291123.99",
"aggregateReduceOnlyQuantity":"0",
"updatedAt":"1705525203015",
"createdAt":"-62135596800000"
}
]
}
{
"positions": [
{
"ticker": "SOL/USDT PERP",
"market_id": "0x95698a9d8ba11660f44d7001d8c6fb191552ece5d9141a05c5d9128711cdc2e0",
"subaccount_id": "0x0ddbe7ea40134ae14ed6a5d104d8783c80663edb000000000000000000000000",
"direction": "short",
"quantity": "135965",
"entry_price": "24074541.242231456624866694",
"margin": "922840147876.471312471775929175",
"liquidation_price": "29392264.1007154944460271",
"mark_price": "102769181.99",
"aggregate_reduce_only_quantity": "0",
"updated_at": 1703361600801,
"created_at": -62135596800000
},
{
"ticker": "SOL/USDT PERP",
"market_id": "0x95698a9d8ba11660f44d7001d8c6fb191552ece5d9141a05c5d9128711cdc2e0",
"subaccount_id": "0x16aef18dbaa341952f1af1795cb49960f68dfee3000000000000000000000000",
"direction": "short",
"quantity": "34.99",
"entry_price": "25000000",
"margin": "963925977.452838924992513061",
"liquidation_price": "50046298.3288514793340278",
"mark_price": "102769181.99",
"aggregate_reduce_only_quantity": "0",
"updated_at": 1703361600801,
"created_at": -62135596800000
},
{
"ticker": "SOL/USDT PERP",
"market_id": "0x95698a9d8ba11660f44d7001d8c6fb191552ece5d9141a05c5d9128711cdc2e0",
"subaccount_id": "0xaf79152ac5df276d9a8e1e2e22822f9713474902000000000000000000000000",
"direction": "short",
"quantity": "160",
"entry_price": "24375243.283322806982741737",
"margin": "3774845657.765183864053125849",
"liquidation_price": "45683836.8041478153648322",
"mark_price": "102769181.99",
"aggregate_reduce_only_quantity": "0",
"updated_at": 1703361600801,
"created_at": -62135596800000
},
{
"ticker": "SOL/USDT PERP",
"market_id": "0x95698a9d8ba11660f44d7001d8c6fb191552ece5d9141a05c5d9128711cdc2e0",
"subaccount_id": "0xce4c01573f2a5f4db5d184b666ecfccb9313cc65000000000000000000000000",
"direction": "short",
"quantity": "8",
"entry_price": "17700000",
"margin": "141673770.450618846533455704",
"liquidation_price": "33723067.9107879579206495",
"mark_price": "102769181.99",
"aggregate_reduce_only_quantity": "0",
"updated_at": 1703361600801,
"created_at": -62135596800000
},
{
"ticker": "INJ/USDT PERP",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0x0000007c60fab7a70c2ae0ebe437f3726b05e7eb000000000000000000000000",
"direction": "short",
"quantity": "0.087829315829932072",
"entry_price": "26453271.813315285838444221",
"margin": "1154483.475338511428917818",
"liquidation_price": "38821468.0751518485185464",
"mark_price": "40421744.55",
"aggregate_reduce_only_quantity": "0",
"updated_at": 1703361600801,
"created_at": -62135596800000
},
{
"ticker": "INJ/USDT PERP",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0x00509ed903475672121d6a1fb2c646eef4da6c44000000000000000000000000",
"direction": "short",
"quantity": "0.022151816236313182",
"entry_price": "15980281.340438795311756833",
"margin": "172410.019375666543049786",
"liquidation_price": "23297442.3538099336017081",
"mark_price": "40421744.55",
"aggregate_reduce_only_quantity": "0",
"updated_at": 1703361600801,
"created_at": -62135596800000
},
{
"ticker": "INJ/USDT PERP",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0x00606da8ef76ca9c36616fa576d1c053bb0f7eb2000000000000000000000000",
"direction": "short",
"quantity": "0.041121486263975195",
"entry_price": "15980281.340438795311756842",
"margin": "320053.045217395063737578",
"liquidation_price": "23297442.3538099336017082",
"mark_price": "40421744.55",
"aggregate_reduce_only_quantity": "0",
"updated_at": 1703361600801,
"created_at": -62135596800000
},
{
"ticker": "INJ/USDT PERP",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0x0154fc01caf0e4c82b7ea6c9be719c260e940ef3000000000000000000000000",
"direction": "short",
"quantity": "0.005684726095134712",
"entry_price": "19468749.344033524349659551",
"margin": "54563.593706555517738922",
"liquidation_price": "28497087.7512237107551938",
"mark_price": "40421744.55",
"aggregate_reduce_only_quantity": "0",
"updated_at": 1703361600801,
"created_at": -62135596800000
},
{
"ticker": "INJ/USDT PERP",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0x021a86b9858f6e4150a724d444e94d10cb75c1b1000000000000000000000000",
"direction": "short",
"quantity": "0.077749303393590607",
"entry_price": "15980281.340438795311756851",
"margin": "605131.369885566651321656",
"liquidation_price": "23297442.3538099336017081",
"mark_price": "40421744.55",
"aggregate_reduce_only_quantity": "0",
"updated_at": 1703361600801,
"created_at": -62135596800000
},
{
"ticker": "INJ/USDT PERP",
"market_id": "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"subaccount_id": "0x03ca8d57285836857dddf42cbb896448015246e7000000000000000000000000",
"direction": "short",
"quantity": "3.933956815705317214",
"entry_price": "20294270.597312410912417203",
"margin": "39442957.950528509629919537",
"liquidation_price": "29726031.3451969744796065",
"mark_price": "40421744.55",
"aggregate_reduce_only_quantity": "0",
"updated_at": 1703361600801,
"created_at": -62135596800000
}
]
}
Parameter | Type | Description |
---|---|---|
positions | DerivativePosition Array | List of liquidable lpositions |
DerivativePosition
Parameter | Type | Description |
---|---|---|
direction | String | Direction of the position (Should be one of: ["long", "short"]) |
market_id | String | ID of the market the position is in |
subaccount_id | String | The subaccount ID the position belongs to |
ticker | String | Ticker of the derivative market |
aggregate_reduce_only_quantity | String | Aggregate quantity of the reduce-only orders associated with the position |
entry_price | String | Entry price of the position |
liquidation_price | String | Liquidation price of the position |
margin | String | Margin of the position |
mark_price | String | Oracle price of the base asset |
quantity | String | Quantity of the position |
updated_at | Integer | Position updated timestamp in UNIX millis |
created_at | Integer | Position created timestamp in UNIX millis. Currently not supported (value will be inaccurate). |
OrderbooksV2
Get an orderbook snapshot for one or more derivative markets.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def main() -> None:
# select network: local, testnet, mainnet
network = Network.testnet()
client = AsyncClient(network)
market_ids = [
"0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"0xd5e4b12b19ecf176e4e14b42944731c27677819d2ed93be4104ad7025529c7ff",
]
orderbooks = await client.fetch_derivative_orderbooks_v2(market_ids=market_ids)
print(orderbooks)
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
)
func main() {
// network := common.LoadNetwork("mainnet", "k8s")
network := common.LoadNetwork("testnet", "lb")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketIds := []string{"0x4ca0f92fc28be0c9761326016b5a1a2177dd6375558365116b5bdda9abc229ce"}
res, err := exchangeClient.GetDerivativeOrderbooksV2(ctx, marketIds)
if err != nil {
panic(err)
}
str, _ := json.MarshalIndent(res, "", " ")
fmt.Print(string(str))
}
Parameter | Type | Description | Required |
---|---|---|---|
market_ids | String Array | List of IDs of markets to get orderbook snapshots from | Yes |
Response Parameters
Response Example:
{
"orderbooks":[
{
"marketId":"0xd5e4b12b19ecf176e4e14b42944731c27677819d2ed93be4104ad7025529c7ff",
"orderbook":{
"sequence":"4099",
"timestamp":"1681978300931",
"buys":[
],
"sells":[
]
}
},
{
"marketId":"0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6",
"orderbook":{
"buys":[
{
"price":"13400000",
"quantity":"683.3032",
"timestamp":"1702002966962"
},
{
"price":"13366500",
"quantity":"108438.1403",
"timestamp":"1702002966962"
}
],
"sells":[
{
"price":"18390200",
"quantity":"78815.8042",
"timestamp":"1702002966962"
}
],
"sequence":"1919116",
"timestamp":"1702003451726"
}
}
]
}
{
"orderbooks": [
{
"market_id": "0x2e94326a421c3f66c15a3b663c7b1ab7fb6a5298b3a57759ecf07f0036793fc9",
"orderbook": {
"buys": [
{
"price": "25100000000",
"quantity": "0.46",
"timestamp": 1701558751779
},
{
"price": "25000000000",
"quantity": "99.51",
"timestamp": 1702216325243
},
{
"price": "24192180000",
"quantity": "0.05",
"timestamp": 1681812143213
},
{
"price": "24000180000",
"quantity": "3",
"timestamp": 1681811751044
},
{
"price": "21295940000",
"quantity": "1",
"timestamp": 1681811119316
},
{
"price": "20160150000",
"quantity": "1",
"timestamp": 1681812143213
},
{
"price": "20000000000",
"quantity": "200",
"timestamp": 1699699685300
},
{
"price": "19894890000",
"quantity": "1",
"timestamp": 1681811751044
},
{
"price": "19764860000",
"quantity": "4",
"timestamp": 1681810803871
},
{
"price": "18900140000",
"quantity": "3",
"timestamp": 1681812143213
},
{
"price": "18439160000",
"quantity": "10",
"timestamp": 1681812143213
},
{
"price": "15000000000",
"quantity": "400",
"timestamp": 1699699705568
},
{
"price": "10000000000",
"quantity": "1000",
"timestamp": 1699699744160
}
],
"sells": [
{
"price": "50501180000",
"quantity": "4",
"timestamp": 1681811955314
},
{
"price": "50198770000",
"quantity": "2.48",
"timestamp": 1681811955314
}
],
"timestamp": -62135596800000
}
}
]
}
Parameter | Type | Description |
---|---|---|
orderbooks | SingleDerivativeLimitOrderbookV2 Array | List of derivative market orderbooks |
SingleDerivativeLimitOrderbookV2
Parameter | Type | Description |
---|---|---|
market_id | String | ID of the market that the orderbook belongs to |
orderbook | DerivativeLimitOrderbookV2 | Orderbook of the market |
DerivativeLimitOrderbookV2
Parameter | Type | Description |
---|---|---|
buys | PriceLevel Array | List of price levels for buys |
sells | PriceLevel Array | List of price levels for sells |
sequence | Integer | Sequence number of the orderbook; increments by 1 each update |
PriceLevel
Parameter | Type | Description |
---|---|---|
quantity | String | Quantity of the price level |
timestamp | Integer | Price level last updated timestamp in UNIX millis |
price | String | Price number of the price level |
StreamOrderbooksV2
Stream orderbook snapshot updates for one or more derivative markets
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from typing import Any, Dict
from grpc import RpcError
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
async def orderbook_event_processor(event: Dict[str, Any]):
print(event)
def stream_error_processor(exception: RpcError):
print(f"There was an error listening to derivative orderbook snapshots ({exception})")
def stream_closed_processor():
print("The derivative orderbook snapshots stream has been closed")
async def main() -> None:
# select network: local, testnet, mainnet
network = Network.testnet()
client = AsyncClient(network)
market_ids = ["0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6"]
task = asyncio.get_event_loop().create_task(
client.listen_derivative_orderbook_snapshots(
market_ids=market_ids,
callback=orderbook_event_processor,
on_end_callback=stream_closed_processor,
on_status_callback=stream_error_processor,
)
)
await asyncio.sleep(delay=60)
task.cancel()
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())
package main
import (
"context"
"fmt"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
)
func main() {
network := common.LoadNetwork("devnet-1", "")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
panic(err)
}
ctx := context.Background()
marketIds := []string{"0x4ca0f92fc28be0c9761326016b5a1a2177dd6375558365116b5bdda9abc229ce"}
stream, err := exchangeClient.StreamDerivativeOrderbookV2(ctx, marketIds)
if err != nil {
panic(err)
}
for {
select {
case <-ctx.Done():
return
default:
res, err := stream.Recv()
if err != nil {
fmt.Println(err)
return
}
fmt.Println(res.MarketId, len(res.Orderbook.Sells), len(res.Orderbook.Buys))
}
}
}
Parameter | Type | Description | Required |
---|---|---|---|
market_ids | String Array | List of market IDs for orderbook streaming; empty means all spot markets | Yes |
callback | Function | Function receiving one parameter (a stream event JSON dictionary) to process each new event | Yes |
on_end_callback | Function | Function with the logic to execute when the stream connection is interrupted | No |
on_status_callback | Function | Function receiving one parameter (the exception) with the logic to execute when an exception happens | No |
Response Parameters
Streaming Response Example:
{
"orderbook":{
"buys":[
{
"price":"13400000",
"quantity":"683.3032",
"timestamp":"1702002966962"
},
...
],
"sells":[
{
"price":"18390200",
"quantity":"78815.8042",
"timestamp":"1702002966962"
},
...
],
"sequence":"1919121",
"timestamp":"1702003627697"
},
"operationType":"update",
"timestamp":"1702003629000",
"marketId":"0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6"
}
Parameter | Type | Description |
---|---|---|
orderbook | DerivativeLimitOrderbookV2 | Orderbook of a Derivative Market |
operation_type | String | Order update type (Should be one of: ["insert", "replace", "update", "invalidate"]) |
timestamp | Integer | Operation timestamp in UNIX millis |
market_id | String | ID of the market the orderbook belongs to |
DerivativeLimitOrderbookV2
Parameter | Type | Description |
---|---|---|
buys | PriceLevel Array | List of price levels for buys |
sells | PriceLevel Array | List of price levels for sells |
sequence | Integer | Sequence number of the orderbook; increments by 1 each update |
PriceLevel
Parameter | Type | Description |
---|---|---|
price | String | Price number of the price level |
quantity | String | Quantity of the price level |
timestamp | Integer | Price level last updated timestamp in UNIX millis |
StreamOrderbookUpdate
Stream incremental orderbook updates for one or more derivative markets. This stream should be started prior to obtaining orderbook snapshots so that no incremental updates are omitted between obtaining a snapshot and starting the update stream.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from decimal import Decimal
from typing import Any, Dict
from grpc import RpcError
from pyinjective.async_client import AsyncClient
from pyinjective.core.network import Network
def stream_error_processor(exception: RpcError):
print(f"There was an error listening to derivative orderbook updates ({exception})")
def stream_closed_processor():
print("The derivative orderbook updates stream has been closed")
class PriceLevel:
def __init__(self, price: Decimal, quantity: Decimal, timestamp: int):
self.price = price
self.quantity = quantity
self.timestamp = timestamp
def __str__(self) -> str:
return "price: {} | quantity: {} | timestamp: {}".format(self.price, self.quantity, self.timestamp)
class Orderbook:
def __init__(self, market_id: str):
self.market_id = market_id
self.sequence = -1
self.levels = {"buys": {}, "sells": {}}
async def load_orderbook_snapshot(async_client: AsyncClient, orderbook: Orderbook):
# load the snapshot
res = await async_client.fetch_derivative_orderbooks_v2(market_ids=[orderbook.market_id])
for snapshot in res["orderbooks"]:
if snapshot["marketId"] != orderbook.market_id:
raise Exception("unexpected snapshot")
orderbook.sequence = int(snapshot["orderbook"]["sequence"])
for buy in snapshot["orderbook"]["buys"]:
orderbook.levels["buys"][buy["price"]] = PriceLevel(
price=Decimal(buy["price"]),
quantity=Decimal(buy["quantity"]),
timestamp=int(buy["timestamp"]),
)
for sell in snapshot["orderbook"]["sells"]:
orderbook.levels["sells"][sell["price"]] = PriceLevel(
price=Decimal(sell["price"]),
quantity=Decimal(sell["quantity"]),
timestamp=int(sell["timestamp"]),
)
break
async def main() -> None:
# select network: local, testnet, mainnet
network = Network.testnet()
async_client = AsyncClient(network)
market_id = "0x17ef48032cb24375ba7c2e39f384e56433bcab20cbee9a7357e4cba2eb00abe6"
orderbook = Orderbook(market_id=market_id)
updates_queue = asyncio.Queue()
tasks = []
async def queue_event(event: Dict[str, Any]):
await updates_queue.put(event)
# start getting price levels updates
task = asyncio.get_event_loop().create_task(
async_client.listen_derivative_orderbook_updates(
market_ids=[market_id],
callback=queue_event,
on_end_callback=stream_closed_processor,
on_status_callback=stream_error_processor,
)
)
tasks.append(task)
# load the snapshot once we are already receiving updates, so we don't miss any
await load_orderbook_snapshot(async_client=async_client, orderbook=orderbook)
task = asyncio.get_event_loop().create_task(
apply_orderbook_update(orderbook=orderbook, updates_queue=updates_queue)
)
tasks.append(task)
await asyncio.sleep(delay=60)
for task in tasks:
task.cancel()
async def apply_orderbook_update(orderbook: Orderbook, updates_queue: asyncio.Queue):
while True:
updates = await updates_queue.get()
update = updates["orderbookLevelUpdates"]
# discard updates older than the snapshot
if int(update["sequence"]) <= orderbook.sequence:
return
print(" * * * * * * * * * * * * * * * * * * *")
# ensure we have not missed any update
if int(update["sequence"]) > (orderbook.sequence + 1):
raise Exception(
"missing orderbook update events from stream, must restart: {} vs {}".format(
update["sequence"], (orderbook.sequence + 1)
)
)
print("updating orderbook with updates at sequence {}".format(update["sequence"]))
# update orderbook
orderbook.sequence = int(update["sequence"])
for direction, levels in {"buys": update["buys"], "sells": update["sells"]}.items():
for level in levels:
if level["isActive"]:
# upsert level
orderbook.levels[direction][level["price"]] = PriceLevel(
price=Decimal(level["price"]), quantity=Decimal(level["quantity"]), timestamp=level["timestamp"]
)
else:
if level["price"] in orderbook.levels[direction]:
del orderbook.levels[direction][level["price"]]
# sort the level numerically
buys = sorted(orderbook.levels["buys"].values(), key=lambda x: x.price, reverse=True)
sells = sorted(orderbook.levels["sells"].values(), key=lambda x: x.price, reverse=True)
# lowest sell price should be higher than the highest buy price
if len(buys) > 0 and len(sells) > 0:
highest_buy = buys[0].price
lowest_sell = sells[-1].price
print("Max buy: {} - Min sell: {}".format(highest_buy, lowest_sell))
if highest_buy >= lowest_sell:
raise Exception("crossed orderbook, must restart")
# for the example, print the list of buys and sells orders.
print("sells")
for k in sells:
print(k)
print("=========")
print("buys")
for k in buys:
print(k)
print("====================================")
if __name__ == "__main__":
asyncio.run(main())
package main
import (
"context"
"fmt"
"sort"
"github.com/InjectiveLabs/sdk-go/client/common"
exchangeclient "github.com/InjectiveLabs/sdk-go/client/exchange"
derivativeExchangePB "github.com/InjectiveLabs/sdk-go/exchange/derivative_exchange_rpc/pb"
"github.com/shopspring/decimal"
)
type MapOrderbook struct {
Sequence uint64
Levels map[bool]map[string]*derivativeExchangePB.PriceLevel
}
func main() {
network := common.LoadNetwork("devnet-1", "")
exchangeClient, err := exchangeclient.NewExchangeClient(network)
if err != nil {
fmt.Println(err)
panic(err)
}
ctx := context.Background()
marketIds := []string{"0x4ca0f92fc28be0c9761326016b5a1a2177dd6375558365116b5bdda9abc229ce"}
stream, err := exchangeClient.StreamDerivativeOrderbookUpdate(ctx, marketIds)
if err != nil {
fmt.Println(err)
panic(err)
}
updatesCh := make(chan *derivativeExchangePB.OrderbookLevelUpdates, 100000)
receiving := make(chan struct{})
var receivingClosed bool
// stream orderbook price levels
go func() {
defer close(updatesCh)
for {
select {
case <-ctx.Done():
return
default:
res, err := stream.Recv()
if err != nil {
fmt.Println(err)
return
}
u := res.OrderbookLevelUpdates
if !receivingClosed {
fmt.Println("receiving updates from stream")
close(receiving)
receivingClosed = true
}
updatesCh <- u
}
}
}()
// ensure we are receiving updates before getting orderbook
fmt.Println("waiting for streaming updates")
<-receiving
// prepare orderbooks map
orderbooks := map[string]*MapOrderbook{}
res, err := exchangeClient.GetDerivativeOrderbooksV2(ctx, marketIds)
if err != nil {
panic(err)
}
for _, ob := range res.Orderbooks {
// init inner maps not ready
_, ok := orderbooks[ob.MarketId]
if !ok {
orderbook := &MapOrderbook{
Sequence: ob.Orderbook.Sequence,
Levels: make(map[bool]map[string]*derivativeExchangePB.PriceLevel),
}
orderbook.Levels[true] = make(map[string]*derivativeExchangePB.PriceLevel)
orderbook.Levels[false] = make(map[string]*derivativeExchangePB.PriceLevel)
orderbooks[ob.MarketId] = orderbook
}
for _, level := range ob.Orderbook.Buys {
orderbooks[ob.MarketId].Levels[true][level.Price] = level
}
for _, level := range ob.Orderbook.Sells {
orderbooks[ob.MarketId].Levels[false][level.Price] = level
}
}
// continuously consume level updates and maintain orderbook
skippedPastEvents := false
for {
updates, ok := <-updatesCh
if !ok {
fmt.Println("updates channel closed, must restart")
return // closed
}
// validate orderbook
orderbook, ok := orderbooks[updates.MarketId]
if !ok {
panic("level update doesn't belong to any orderbooks")
}
// skip if update sequence <= orderbook sequence until it's ready to consume
if !skippedPastEvents {
if orderbook.Sequence >= updates.Sequence {
continue
}
skippedPastEvents = true
}
// panic if update sequence > orderbook sequence + 1
if updates.Sequence > orderbook.Sequence+1 {
fmt.Printf("skipping levels: update sequence %d vs orderbook sequence %d\n", updates.Sequence, orderbook.Sequence)
panic("missing orderbook update events from stream, must restart")
}
// update orderbook map
orderbook.Sequence = updates.Sequence
for isBuy, update := range map[bool][]*derivativeExchangePB.PriceLevelUpdate{
true: updates.Buys,
false: updates.Sells,
} {
for _, level := range update {
if level.IsActive {
// upsert
orderbook.Levels[isBuy][level.Price] = &derivativeExchangePB.PriceLevel{
Price: level.Price,
Quantity: level.Quantity,
Timestamp: level.Timestamp,
}
} else {
// remove inactive level
delete(orderbook.Levels[isBuy], level.Price)
}
}
}
// construct orderbook arrays
sells, buys := maintainOrderbook(orderbook.Levels)
fmt.Println("after", orderbook.Sequence, len(sells), len(buys))
if len(sells) > 0 && len(buys) > 0 {
// assert orderbook
topBuyPrice := decimal.RequireFromString(buys[0].Price)
lowestSellPrice := decimal.RequireFromString(sells[0].Price)
if topBuyPrice.GreaterThanOrEqual(lowestSellPrice) {
panic(fmt.Errorf("crossed orderbook, must restart: buy %s >= %s sell", topBuyPrice, lowestSellPrice))
}
}
res, _ = exchangeClient.GetDerivativeOrderbooksV2(ctx, marketIds)
fmt.Println("query", res.Orderbooks[0].Orderbook.Sequence, len(res.Orderbooks[0].Orderbook.Sells), len(res.Orderbooks[0].Orderbook.Buys))
// print orderbook
fmt.Println(" [SELLS] ========")
for _, s := range sells {
fmt.Println(s)
}
fmt.Println(" [BUYS] ========")
for _, b := range buys {
fmt.Println(b)
}
fmt.Println("=======================================================")
}
}
func maintainOrderbook(orderbook map[bool]map[string]*derivativeExchangePB.PriceLevel) (buys, sells []*derivativeExchangePB.PriceLevel) {
for _, v := range orderbook[false] {
sells = append(sells, v)
}
for _, v := range orderbook[true] {
buys = append(buys, v)
}
sort.Slice(sells, func(i, j int) bool {
return decimal.RequireFromString(sells[i].Price).LessThan(decimal.RequireFromString(sells[j].Price))
})
sort.Slice(buys, func(i, j int) bool {
return decimal.RequireFromString(buys[i].Price).GreaterThan(decimal.RequireFromString(buys[j].Price))
})
return sells, buys
}
Parameter | Type | Description | Required |
---|---|---|---|
market_ids | String Array | List of market IDs for orderbook streaming; empty means all derivative markets | Yes |
callback | Function | Function receiving one parameter (a stream event JSON dictionary) to process each new event | Yes |
on_end_callback | Function | Function with the logic to execute when the stream connection is interrupted | No |
on_status_callback | Function | Function receiving one parameter (the exception) with the logic to execute when an exception happens | No |
Response Parameters
Streaming Response Example:
* * * * * * * * * * * * * * * * * * *
updating orderbook with updates at sequence 589
Max buy: 10000000000 - Min sell: 50000000000
sells
price: 101000000000 | quantity: 0.0007 | timestamp: 1675291761230
price: 100000000000 | quantity: 0.0037 | timestamp: 1675291786816
price: 70000000000 | quantity: 0.0001 | timestamp: 1671787246665
price: 65111000000 | quantity: 0.0449 | timestamp: 1675291786816
price: 50000000000 | quantity: 0.1 | timestamp: 1676326399734
=========
buys
price: 10000000000 | quantity: 0.0004 | timestamp: 1676622014694
price: 5000000000 | quantity: 0.0097 | timestamp: 1676383776468
price: 1000000000 | quantity: 0.0013 | timestamp: 1676622213616
====================================
* * * * * * * * * * * * * * * * * * *
updating orderbook with updates at sequence 590
Max buy: 10000000000 - Min sell: 50000000000
sells
price: 101000000000 | quantity: 0.0007 | timestamp: 1675291761230
price: 100000000000 | quantity: 0.0037 | timestamp: 1675291786816
price: 70000000000 | quantity: 0.0001 | timestamp: 1671787246665
price: 65111000000 | quantity: 0.0449 | timestamp: 1675291786816
price: 50000000000 | quantity: 0.1 | timestamp: 1676326399734
=========
buys
price: 10000000000 | quantity: 0.0004 | timestamp: 1676622014694
price: 5000000000 | quantity: 0.0097 | timestamp: 1676383776468
price: 1000000000 | quantity: 0.0014 | timestamp: 1676622220695
====================================
Parameter | Type | Description |
---|---|---|
orderbook_level_updates | OrderbookLevelUpdates | Orderbook level updates of a derivative market |
operation_type | String | Order update type (Should be one of: ["insert", "replace", "update", "invalidate"]) |
timestamp | Integer | Operation timestamp in UNIX millis |
market_id | String | ID of the market the orderbook belongs to |
OrderbookLevelUpdates
Parameter | Type | Description |
---|---|---|
market_id | String | ID of the market the orderbook belongs to |
sequence | Integer | Orderbook update sequence number; increments by 1 each update |
buys | PriceLevelUpdate Array | List of buy level updates |
sells | PriceLevelUpdate Array | List of sell level updates |
updated_at | Integer | Timestamp of the updates in UNIX millis |
PriceLevelUpdate
Parameter | Type | Description |
---|---|---|
price | String | Price number of the price level |
quantity | String | Quantity of the price level |
is_active | Boolean | Price level status |
timestamp | Integer | Price level last updated timestamp in UNIX millis |
SubaccountOrdersList
Get the derivative orders of a specific subaccount.
IP rate limit group: indexer
Request Parameters
Request Example:
import asyncio
from pyinjective.async_client import AsyncClient
from pyinjective.client.model.pagination import PaginationOption
from pyinjective.core.network import Network
async def main() -> None:
# select network: local, testnet, mainnet
network