NAV
python

Introduction

Frontrunner is the first zero gas fee, decentralized sports prediction market built on blockchain where you get the best odds with no house edge. Frontrunner is powered by Injective, a blockchain built for Web3 finance applications. This SDK offers a programmatic way to interact with both, powering use cases such as market making.

Please join our Discord Server to ask questions and become a part of our community.

Quickstart

To demonstrate SDK usage, we'll be using it to create a wallet, place a market order, and listing our orders.

Installation

First install the Injective SDK pre-requisites.

pip install frontrunner-sdk

Then install the Frontrunner SDK.

Contact support@getfrontrunner.com for a Frontrunner API Key. Keep this somewhere safe. When launching a Python REPL or running the scripts in this guide, make sure that API Key is set in the environment variable FR_PARTNER_API_TOKEN.

Creating and Funding a Wallet

from frontrunner_sdk import FrontrunnerSDK

# Create a synchronous Frontrunner SDK
# By default, this will use testnet; no "real" tokens will be involved.
sdk = FrontrunnerSDK()

# Creates a new wallet locally
# Requests funds from an injective faucet
create_wallet = sdk.injective.create_wallet()

# Save your wallet credentials
print(f"""
Put this somewhere safe!

    {create_wallet.wallet.mnemonic}

""")

Output

Put this somewhere safe!

    bip39 bip39 bip39 bip39 bip39 bip39 bip39 bip39 bip39 bip39 bip39 bip39 bip39 bip39 bip39 bip39 bip39 bip39 bip39 bip39 bip39 bip39 bip39 bip39

In a script or Python REPL, run the following code.

This will create an instance of the SDK which will be used to interact with both Frontrunner and Injective. There are two "versions" of this SDK: synchronous and asynchronous. For this quickstart, we will use the synchronous version.

Then, we'll call create_wallet to create our wallet and receive an airdrop from Injective's faucet, to allow us to start placing orders right away! Any subsequent calls that require a wallet will use the one we just made.

Getting Frontrunner Markets

find_markets = sdk.frontrunner.find_markets(
  sports=["basketball"], # Looking for basketball game markets
  event_types=["game"], # Looking for game (instead of future) markets
  prop_types=["winner"], # Looking for winner (instead of other) markets
  market_statuses=["active"], # Only active markets
)

# Pick a market
market = find_markets.markets[0]
print(f"Market: {market.long_entity_name} [{market.prop_name}] vs {market.short_entity_name}")

Output

Market: New York Knicks [Winner] vs Cleveland Cavaliers

Before we can bet on markets, we'll need to find them. The example here finds all markets where the sport is basketball, the event is a game, and the proposition is for a winner. The response object contains the raw market objects and the market IDs, which will be useful for placing bets.

Then, we'll pick one market to place bets on, and print some info about it.

View an Order Book

# get the order book for this market
response = sdk.injective.get_order_books([market.injective_id])
order_book = response.order_books[market.injective_id]

# Frontrunner testnet markets are in FRCOIN and on Injective, FRCOIN has 6 decimals.
# 1,000,000 from Injective is $1 FRCOIN.
FRCOIN_SCALE_FACTOR = 10 ** 6

# print order book buys
print("buys:")
for buy in order_book.buys:
  print(f"  {buy.quantity} @ ${int(buy.price) / FRCOIN_SCALE_FACTOR}")

# print order book sells
print("sells:")
for sell in order_book.sells:
  print(f"  {sell.quantity} @ ${int(sell.price) / FRCOIN_SCALE_FACTOR}")

# find the highest buy and lowest sell
buy_prices = [int(order.price) / FRCOIN_SCALE_FACTOR for order in order_book.buys]
sell_prices = [int(order.price) / FRCOIN_SCALE_FACTOR for order in order_book.sells]
highest_buy, lowest_sell = max(buy_prices), min(sell_prices)
print(f"bid-ask spread: [${highest_buy}, ${lowest_sell}]")

Output

buys:
  5882 @ $0.34
  9091 @ $0.33
  15625 @ $0.32
  5000 @ $0.1
sells:
  4364 @ $0.36
  8108 @ $0.37
  13158 @ $0.38
bid-ask spread: [$0.34, $0.36]

Without knowing much else about the market besides its ID, it's hard to price bets and make orders. Here, we'll place multiple buy orders above the highest buy price.

We'll call get_order_books, passing in the Injective market id, to get the current order book. This order book contains both the buys and sells. Using the buys, we can find the highest buy price.

Placing buy orders

from frontrunner_sdk.models import Order

highest_buy = 0.01
injective_id = "0xd03091c74e4e76878c2afbeb470b1c825677014afdaa3d315fa534884d2d90e1"

create_orders = sdk.injective.create_orders([
    Order.buy_long(injective_id, 10, highest_buy + 0.01),
    Order.buy_long(injective_id, 5, highest_buy + 0.02),
])

print(f"""
Transaction: {create_orders.transaction}

You can view your transaction at:

  https://testnet.explorer.injective.network/transaction/{create_orders.transaction}
""")

Output

Transaction: 917F980F001120A05642F225E9197CCDF1BB5677A6381F81D2CC95410466008C

You can view your transaction at:

  https://testnet.explorer.injective.network/transaction/917F980F001120A05642F225E9197CCDF1BB5677A6381F81D2CC95410466008C

To place the orders, we'll call create_orders. We'll place...

Note that we use a hard-coded market ID here that points to a testnet USDT market that can be traded in with Injective Faucet funds.

Contact support@getfrontrunner.com to request testnet FRCOIN to trade in real Frontrunner markets.

Retrieving Your Orders

get_orders = sdk.injective.get_orders(mine=True, execution_types=["limit"])

print("orders:")
for order_history in get_orders.orders:
  print(f"  {order_history.order_type} {order_history.order.order_hash}: {order_history.order.quantity} @ ${int(order_history.order.price) / FRCOIN_SCALE_FACTOR}")

Output

orders:
  OrderType.BUY_LONG 0x642c138d85a8224093665d1c8bd4fc31e2307fcee62157c7175e5865ea850247: 5 @ $0.03
  OrderType.BUY_LONG 0xd82ced802fdf5ce55a1b37238d83ece7677e4aeb0d576ebb76d041a590ecff16: 10 @ $0.02

Concepts

Order Types

Participants in Frontrunner markets submit buy (a.k.a. bid) and sell (a.k.a. ask) orders of two different types:

Market Types

Binary markets

In Frontrunner's binary markets, both the long and the short side represent distinct entities. All game markets that cannot end in a draw are binary markets because there are only two possible standard outcomes.

For example, the game winner market Miami Heat @ Orlando Magic is a binary market where the Heat are the long_entity and the Magic are the short_entity.

For binary markets, long positions resolve at $1 (and short positions at $0) if the outcome represented by the long_entity occurs; short positions resolve at $1 (and long positions at $0) if the outcome represented by the short_entity occurs.

Non-Binary Markets

Non-binary markets are represented by a group of binary markets that do not have short_entity specified. When there is no short_entity, taking a short position is simply betting against the long outcome. All game markets that can end in a draw (besides NFL) and futures markets are non-binary markets.

For example, the game winner market Arsenal FC v Chelsea FC is a non-binary market represented by 3 distinct Frontrunner markets:

Futures markets may exist for season winners like the NFL Championship, Premier League Season, and NBA Finals. For futures markets, one market exists for each entity with sufficient chances of winning (at the discretion of Frontrunner).

Home and Away

Frontrunner defines the long entity as the first entity in the matchup description. This definition changes depending on the sport - the different options are listed below.

League Sport Event Name Long Entity Short Entity
EPL Home v Away n/a* n/a
Formula 1 n/a** n/a* n/a
MLB Away @ Home Away Home
NBA Away @ Home Away Home
NFL Away @ Home Away Home

*The home/away designation does correlate directly to long/short entities for non-binary markets like EPL markets (as well as futures) due to their structure. However, the home team can still be detected based on the Sport Event Name. See the Non-Binary Markets section above for additional context.

**Formula 1 information from Frontrunner does not include information about player/team nationalities.

Configuring

SDK Parameters

There are a few ways to configure values for the SDK. In order of precedence, they are as follows:

  1. Direct named environment variables
  2. Presets via environment variables
  3. Default values

Wallet

This specifies which wallet the SDK will use for mutating the blockchain and querying the blockchain for "my" data. These variables are checked in order.

Environment Variables

  1. FR_WALLET_MNEMONIC
  2. FR_WALLET_PRIVATE_KEY_HEX

Default

None

Frontrunner Base API URL

This is the base URL for Frontrunner-specific operations such as finding markets.

Environment Variables

Default

https://partner-api-testnet.getfrontrunner.com/api/v1

Frontrunner API Token

To interact with the Frontrunner APIs, you will need a token to authenticate. Requests without authentication will fail.

Environment Variable

Default

None

Injective Network

The "network" is which blockchain network to use. Valid values are...

For testing with "play" money and developing code, use testnet. For production and "real" money, use mainnet.

The chain ID must match with the Injective network you are using.

Network Chain ID
testnet injective-888
mainnet injective-1

Environment Variable

Default

Network is testnet, and Chain ID is injective-888.

Injective Endpoints

Injective is built on the Cosmos network. Cosmos defines a set of well known endpoint types that are supported for interacting with the blockchain. You can learn more about them here: Cosmos Good-To-Know Dev Terms.

In Cosmos, we have the following endpoints:

Normally, these come in sets, and are not interchangeable between sets.

Environment Variables

Defaults

Frontrunner does not run the Injective Explorer, so Explorer Authority defaults are always Injective's.

Testnet

Endpoint Type Default Value
Exchange Authority injective-node-testnet.grpc-exchange.getfrontrunner.com:443
Explorer Authority k8s.testnet.explorer.grpc.injective.network:443
LCD Base URL https://injective-node-testnet.lcd.getfrontrunner.com
RPC Base URL wss://injective-node-testnet.tm.getfrontrunner.com/websocket
gRPC Authority injective-node-testnet.grpc.getfrontrunner.com:443

Mainnet

Endpoint Type Default Value
Exchange Authority k8s.global.mainnet.exchange.grpc.injective.network:443
Explorer Authority k8s.global.mainnet.explorer.grpc.injective.network:443
LCD Base URL https://k8s.global.mainnet.lcd.injective.network:443
RPC Base URL wss://k8s.global.mainnet.tm.injective.network:443/websocket
gRPC Authority k8s.global.mainnet.chain.grpc.injective.network:443

Presets

The preset endpoint groups for Injective are taken from Injective's Network class here.

Injective Testnet K8s

When the environment variable FR_PRESET_NODES is set to injective-k8s and the SDK is configured for testnet, Injective's Kubernetes-based endpoints on the testnet network (Network.testnet()) will be used instead of the defaults above.

Injective Mainnet Global

When the environment variable FR_PRESET_NODES is set to injective-global and the SDK is configured for mainnet, Injective's global, load-balanced endpoints on the mainnet network (Network.mainnet("lb")) will be used.

Injective Mainnet Sentry

When the environment variable FR_PRESET_NODES is set to injective-sentry and the SDK is configured for mainnet, one of Injective's standalone sentry nodes on the mainnet network (Network.mainnet("sentry0")) will be used.

SSL

The environment variable FR_INJECTIVE_INSECURE controls whether or not an insecure/plaintext connection is made to Injective endpoints.
Preset node groups have this set correctly by default (see below).
Set this environment variable to "true" or "false" as needed.

Preset Insecure?
Frontrunner Testnet False
Frontrunner Mainnet False
Injective Testnet K8s False
Injective Mainnet Global False
Injective Mainnet Sentry True
FR_PRESET_NODES unset False

Injective Faucet

A faucet is a site that dispenses free tokens to a wallet. Faucets are used to acquire tokens without involving "real" money or mining them yourself.
This faucet is only relevant for testnet - there is no faucet on mainnet.

Environment Variable

Default

https://knroo5qf2e.execute-api.us-east-2.amazonaws.com/default/TestnetFaucetAPI

Configuring Logging

All actions within the SDK are logged via Python's logging module. The logging module is hierarchical, so you can selectively turn off sections of a module by name.

import logging

# Set logging for frontrunner-sdk to INFO and above
logging.getLogger("frontrunner_sdk").setLevel(logging.INFO)

# Only log critical errors from config and clients
logging.getLogger("frontrunner_sdk.clients").setLevel(logging.CRITICAL)
logging.getLogger("frontrunner_sdk.config").setLevel(logging.CRITICAL)

The default logging level is the same as logging's defaults.

For more information on configuring Python's logging, see the Python Logging Cookbook.

Technical Conventions

Sync and Async SDKs

This SDK is designed with async/concurrent programming in mind. However, it can be difficult to use in regular scripts because the way async code needs to be written requires an all-or-nothing approach. To cater to non-async code, we provide a version of the SDK that can be used without async.

from frontrunner_sdk import FrontrunnerSDKAsync

sdk = FrontrunnerSDKAsync()

To create an async version of the SDK, use FrontrunnerSDKAsync.

from frontrunner_sdk import FrontrunnerSDK

sdk = FrontrunnerSDK()

To create a non-async version of the SDK, use FrontrunnerSDK.

Response Objects

SDK return values are always wrapped in a response object. The response object contains the desired values eg. orders, positions, portfolio info. This is to mitigate the effects of SDK changes for developers.

sdk = FrontrunnerSDK()

# SDK return values are always response objects
response = sdk.injective.some_list_operation()

# Desired values will be Within the response object
desired_value = response.desired_value
maybe_other_value = response.maybe_other_value

API Namespaces

Within the SDK, we split the operations into the following namespaces:

This split exists so that the origin of the call data is transparent. For example, if you only wish to interact with Frontrunner services, only use the calls available in the frontrunner namespace in the SDK.

To view the Frontrunner REST API Swagger docs, load openapi/frontrunner-api/openapi.json into https://editor.swagger.io.
To view the Injective API docs, see https://api.injective.exchange/#introduction.

Exceptions

In general, this SDK wraps downstream exceptions in our own exception classes. Also, each exception class has a specific meaning.

Class Meaning
FrontrunnerException Root exception for anything raised by this SDK
FrontrunnerUserException User is at fault
FrontrunnerExternalException User is not at fault
FrontrunnerConfigurationException User did not configure or misconfigured a value required by the SDK
FrontrunnerArgumentException User provided an invalid value to an operation
FrontrunnerInjectiveException User provided an invalid value to an Injective API
FrontrunnerUnserviceableException Service is not usable eg. responds with 5xx status code

Logging

All actions within the SDK are logged via Python's logging module. Each log level has a specific meaning.

CRITICAL logs indicate that the SDK cannot function. Examples include unreachable endpoints, inoperable endpoints, incorrect credentials, and system errors. Typically, these are logged alongside a raised FrontrunnerExternalException.

ERROR logs are generated alongisde every raised exception ie. logger.exception(...).

WARNING logs indicate a given operation, parameter, or configuration is deprecated, and should not be used. The warning will include an alternative to use.

INFO logs are generated after every completed SDK operation. These logs serve as an audit trail of what happened in the SDK.

DEBUG logs are generated around (before + after) every external API call, before calling an external process, and after configuration is read. With debug logs, there should be enough detail to recreate the sequence of events that lead to a specific failure at the SDK level.

Gas and Fees

Gas represents the computational effor required to execute an operation. The fee is how much INJ to pay to execute a transaction. Insufficient gas will result in a failed transaction, and consumed gas.

The fee amount is fee = gas * gas price where gas price is an arbitrary constant.

To learn more, see Gas and Fees from the Injective and Cosmos documentation.

Gas Estimation

Injective docs suggest using simulation to estimate the amount of gas required to execute a specific transaction. However, simulation is slow (around 2 seconds). For HFT use cases, this SDK uses a precomputed lookup table to estimate the required gas. Because of the way the underlying Cosmos SDK works, gas is strongly tied to the size of the message and is also time-independent. That property allows us to roughly estimate the amount of gas to send based on the type of message and number of orders it contains.

To ensure there is enough gas, and to account for errors in estimation, a flat gas "buffer" amount is also added.

For HFT and API trading notes, see this Injective note.

Subaccount Management

Definition

Subaccounts enable traders to execute transactions through multiple accounts under the same wallet. Each subaccount has independent balances, margin and positions and can trade independently. so they can be used to isolate positions or margin or to run multiple strategies independently. All orders in Injective markets are from a subaccount (often the default subaccount).

Subaccounts are 0-indexed, and the default subaccount, subaccount 0, has a special property: trading from the default subaccount draws funds from the main bank balance. Detailed information about this feature can be found here. The main bank balance is associated with the Injective address (e.g. inj14w0zfp47jqpgjst87vxg5ydgvtevfdm38338xp). Subaccount balances are associated with each subaccount (e.g. 0xb4efdbe3240d3d2a1bc6be8a1f717944e734a0dd000000000000000000000000).

Properties

Transferring Funds

These are the operations involved in transferring funds between accounts or subaccounts:

Operation Usage Notes
fund_external_subaccount Send funds to a subaccount that is NOT owned by the SDK wallet. Cannot fund subaccount 0.
fund_external_wallet Send funds to a another Injective wallet. This sends from the configured SDK wallet's main bank balance to the external wallet's main bank balance
fund_subaccount Send funds from the main bank balance to a subaccount that IS owned by the SDK wallet. Or send funds between subaccounts that ARE BOTH owned by the SDK wallet. Cannot fund subaccount 0. Funding from source subaccount 0 sends from the main bank balance.
withdraw_from_subaccount Withdraw funds from a subaccount that IS owned by the SDK wallet to the main bank balance. Cannot withdraw from subaccount 0.

Where operations involve subaccounts owned by the SDK wallet, either a Subaccount object or an integer index can be provided for convenience.
A Subaccount object can be created in a few different ways:

from frontrunner_sdk.models import Subaccount, Wallet

wallet = Wallet._new()

Subaccount.from_subaccount_id("0xb4efdbe3240d3d2a1bc6be8a1f717944e734a0dd000000000000000000000001")
wallet.subaccount(1)
Subaccount.from_injective_address_and_index("inj1knhahceyp57j5x7xh69p7utegnnnfgxavmahjr", 1)
Subaccount.from_ethereum_address_and_index("0xb4efdbe3240d3d2a1bc6be8a1f717944e734a0dd", 1)

Sample Code: Market Making

A simple setup to create liquidity on both sides of a Frontrunner market involves using two non-default subaccounts, where one subaccount creates long orders and another creates short orders.

For example, this is how two subaccounts could be funded with testnet tokens and then used to submit orders on both sides:

import time
from typing import List

from frontrunner_sdk import FrontrunnerSDKAsync
from frontrunner_sdk.models import Order, OrderType

FRCOIN_SCALE_FACTOR = 10 ** 6


async def _print_orderbook(sdk: FrontrunnerSDKAsync, injective_market_id: str):
  response = await sdk.injective.get_order_books([injective_market_id])
  order_book = response.order_books[injective_market_id]

  print("buys:")
  for buy in order_book.buys:
    print(f"  {buy.quantity} @ ${int(buy.price) / FRCOIN_SCALE_FACTOR}")
  print("sells:")
  for sell in order_book.sells:
    print(f"  {sell.quantity} @ ${int(sell.price) / FRCOIN_SCALE_FACTOR}")

  buy_prices = [int(order.price) / FRCOIN_SCALE_FACTOR for order in order_book.buys]
  sell_prices = [int(order.price) / FRCOIN_SCALE_FACTOR for order in order_book.sells]
  highest_buy, lowest_sell = max(buy_prices) if buy_prices else 0, min(sell_prices) if sell_prices else 0
  print(f"bid-ask spread: [${highest_buy}, ${lowest_sell}]")

async def _print_orders(sdk: FrontrunnerSDKAsync, injective_market_id: str, subaccount_indexes: List[int]):
  for subaccount_index in subaccount_indexes:
    get_orders = await sdk.injective.get_orders(mine=True, market_ids=[injective_market_id], subaccount_index=subaccount_index)
    print(f"orders for index {subaccount_index}:")
    for order_history in get_orders.orders:
      print(f"{order_history.order_type} {order_history.order.state} : filled {order_history.order.filled_quantity} / {order_history.order.quantity} @ ${int(float(order_history.order.price)) / FRCOIN_SCALE_FACTOR}")

def calculate_price(probability: float, price_margin: float, order_type: OrderType):
  if order_type == OrderType.BUY_LONG:
    return round(max(0.01, min(0.99, probability - price_margin)), 2)
  else:
    return round(min(0.99, max(0.01, probability + price_margin)), 2)

def generate_histogram_orders(injective_market_id: str, total_value_coefficients: List[float], order_type: OrderType, price: float, price_step: float, total_value: float, subaccount_index: int):
  orders = []
  for coeff in total_value_coefficients:
    if price < 0.01 or price > 0.99:
      break
    coeff_total_value = total_value * coeff # total amount we want to spend on shares at this price
    num_shares = coeff_total_value / price # num_shares * price = total_value spent
    if order_type == OrderType.BUY_LONG:
      orders.append(Order.buy_long(injective_market_id, int(round(num_shares, 0)), round(price, 2), subaccount_index=subaccount_index))
    elif order_type == OrderType.BUY_SHORT:
      orders.append(Order.buy_short(injective_market_id, int(round(num_shares, 0)), round(price, 2), subaccount_index=subaccount_index))
    price += price_step
  return orders

async def main():
  total_value_coefficients = [0.2, 0.3, 0.5] # the distribution of funds for each price step; sum should always equal 1
  assert(int(sum(total_value_coefficients)) == 1)
  sdk = FrontrunnerSDKAsync()
  print(f"Running with wallet {(await sdk.wallet()).injective_address}")
  injective_market_id = "REPLACE_ME"

  wallet = await sdk.wallet()
  long_subaccount_index = 1
  short_subaccount_index = 2
  long_subaccount = wallet.subaccount_address(long_subaccount_index)
  short_subaccount = wallet.subaccount_address(short_subaccount_index)
  print(f"Running with wallet {wallet.injective_address}. {long_subaccount=}, {short_subaccount=}")

  response = await sdk.injective.fund_subaccount(1000, "FRCOIN", destination_subaccount_index=long_subaccount_index)
  print(f"View deposit transaction to long side subaccount: https://testnet.explorer.injective.network/transaction/{response.transaction}")

  response = await sdk.injective.fund_subaccount(1000, "FRCOIN", destination_subaccount_index=short_subaccount_index)
  print(f"View deposit transaction to short side subaccount: https://testnet.explorer.injective.network/transaction/{response.transaction}")

  probability = 0.68
  desired_total_liquidity = 100
  spread_per_side = 0.01
  long_base_price = calculate_price(probability, spread_per_side, OrderType.BUY_LONG)
  short_base_price = calculate_price(probability, spread_per_side, OrderType.BUY_SHORT)
  # Note this includes validation of the probability to prevent submitting orders at invalid prices.
  #   Instead, this case could be detected and a smaller histogram created.
  buy_long_orders = generate_histogram_orders(injective_market_id, total_value_coefficients, OrderType.BUY_LONG, long_base_price, -1 * spread_per_side, desired_total_liquidity / 2, long_subaccount_index) if probability >= 0.02 else []
  buy_short_orders = generate_histogram_orders(injective_market_id, total_value_coefficients, OrderType.BUY_SHORT, short_base_price, spread_per_side, desired_total_liquidity / 2, short_subaccount_index)  if probability <= 0.98 else []
  orders = buy_long_orders + buy_short_orders
  print(orders)
  response = await sdk.injective.create_orders(orders)
  print(f"View create orders transaction: https://testnet.explorer.injective.network/transaction/{response.transaction}")
  time.sleep(5)  # ensure new state is reflected
  await _print_orderbook(sdk, injective_market_id)
  await _print_orders(sdk, injective_market_id, list(range(3)))

Operations

Frontrunner: Find Markets

Enhanced search of Frontrunner markets.

Parameters

# Find Frontrunner markets where...
response = sdk.frontrunner.find_markets(
  # ...the winner
  prop_type=["winner"],
  # ...of a basketball
  sports=["basketball"],
  # ...game
  event_types=["game"],
  # ...is the Los Angeles Lakers (LAL)
  sport_entity_abbreviations=["LAL"],
)

# Find Frontrunner markets where...
response = sdk.frontrunner.find_markets(
  # ...formula one
  sports=["formula1"],
  # ...team Williams or Alfa Romeo
  sport_entity_names=["Williams", "Alfa Romeo"],
  # ...is not for a 'winner' type prop
  prop_types=["other"],
)

# Get all active Frontrunner markets
response = sdk.frontrunner.find_markets()
Name Type Req? Description
sports [str] Only include the given sports
league_names [str] Only include the given league names
event_types [str] Only include the given event types; must be in [game, future]
sport_entity_names [str] Only include the given entity names (eg. Atlanta Hawks)
sport_entity_abbreviations [str] Only include the given entity abbreviations (eg. ATL)
prop_types [str] Only include the given prop types; must be in [winner, other]
market_statuses [str] Only include the given market statuses; must be in [active, closed]

Response

for market in response.markets:
    print(f"Market: {market.address} [{market.long_entity_name} / {market.short_entity_name}]")
Name Type Description
market_ids [str] Related market IDs, from sport entities, props, and market_statuses constraints
markets [Market] Related market objects

Frontrunner: Get Leagues

Get Frontrunner leagues.

Parameters

# Get all Frontrunner leagues
response = sdk.frontrunner.get_leagues()
Name Type Req? Description
id str Only include the specific league
sport str Only include the leagues for the given sport

Response

print(f"Leagues: {response.leagues}")
Name Type Description
leagues [League] Related league objects

Frontrunner: Get Markets

Get Frontrunner markets.

Parameters

from frontrunner_sdk.openapi.frontrunner_api import MarketStatus

# Get all active NFL markets
league_id = "c6c521c4-88d0-463d-aa2a-fe3765ec872a"
response = sdk.frontrunner.get_markets(
    league_id=league_id,
    status=MarketStatus.ACTIVE,
)

# Get all active Frontrunner markets
response = sdk.frontrunner.get_markets()
Name Type Req? Description
id str Only include the specific market
injective_id str Only include the market corresponding to the given Injective market
prop_id str Only include markets for the given prop
event_id str Only include markets for the given event
league_id str Only include markets for the given league
status str Only include the given market status; must be one of [active, closed]

Response

for market in response.markets:
    print(f"Market: {market.address} [{market.long_entity_name} / {market.short_entity_name}]")
Name Type Description
markets [Market] Related market objects

Frontrunner: Get Props

Get Frontrunner props.

Parameters

# Get all NFL props
league_id = "c6c521c4-88d0-463d-aa2a-fe3765ec872a"
response = sdk.frontrunner.get_props(
    league_id=league_id,
)

# Get all Frontrunner props
response = sdk.frontrunner.get_props()
Name Type Req? Description
id str Only include the specific prop
league_id str Only include the props for the given league

Response

print(f"Props: {response.props}")
Name Type Description
props [Prop] Related prop objects

Frontrunner: Get Sport Entities

Get Frontrunner sport entities (i.e. teams, players, etc.)

Parameters

# Get all NFL sport entities
league_id = "c6c521c4-88d0-463d-aa2a-fe3765ec872a"
response = sdk.frontrunner.get_sport_entities(
    league_id=league_id,
)

# Get all Frontrunner sport entities
response = sdk.frontrunner.get_sport_entities()
Name Type Req? Description
id str Only include the specific sport entity
league_id str Only include the sport entities for the given league
sport str Only include the sport entities for the given sport

Response

print(f"Sport Entities: {response.sport_entities}")
Name Type Description
sport_entities [SportEntity] Related sport entity objects

Frontrunner: Get Sport Events

Get Frontrunner sport events

Parameters

# Get all NFL sport events
league_id = "c6c521c4-88d0-463d-aa2a-fe3765ec872a"
response = sdk.frontrunner.get_sport_events(
    league_id=league_id,
)

# Get all Frontrunner sport events
response = sdk.frontrunner.get_sport_events()
Name Type Req? Description
id str Only include the specific sport event
league_id str Only include the sport events for the given league
sport str Only include the sport events for the given sport
starts_since datetime Only include the sport events starting after the given datetime

Response

print(f"Sport Events: {response.sport_events}")
Name Type Description
sport_events [SportEvent] Related sport event objects

Frontrunner: Get Sports

Get supported sports on Frontrunner

Parameters

# Get all Frontrunner sports
response = sdk.frontrunner.get_sports()

No parameters. Note that this is convenience operation that collects all sports from get_leagues.

Response

print(f"Sports: {response.sports}")
Name Type Description
sports [str] All sport names

Injective: Cancel All Orders

Cancel all your open orders.

Parameters

# Cancel all your orders
response = sdk.injective.cancel_all_orders()
Name Type Req? Description
subaccount_index int 0 Index of the subaccount to cancel orders for

Response

print("transaction:", response.transaction)
print("orders:")
for order in response.orders:
  print(f"\tmarket: {order.market_id[:12]}... price: {order.price} quantity: {order.quantity}")
Name Type Description
orders Iterable[DerivativeLimitOrder] Cancelled orders
transaction Optional[str] Transaction ID of the order cancellation

Injective: Cancel Orders

Cancels specific orders.

Parameters

# Cancel a single specific order
response = sdk.injective.cancel_orders([
  CancelOrder(
    market_id="0x141e3c92ed55107067ceb60ee412b86256cedef67b1227d6367b4cdf30c55a74",
    order_hash="znihpTtkWn/9Npy1Zzku+GNxXfyLE/k05U1KXRUTO1E=",
  ),
])
Name Type Req? Description
orders [CancelOrder] List of order cancellation specs
orders[].market_id str Market ID of order to cancel
orders[].order_hash str Order hash of order to cancel
orders[].subaccount_index int 0 Index of the subaccount to cancel the order for

Response

print("transaction:", response.transaction)
Name Type Description
transaction str Transaction ID of the order cancellation

Injective: Create Orders

Create orders. Order has a market, price, and quantity.

For finding markets, see find_markets.

Parameters

some_market = "0x141e3c92ed55107067ceb60ee412b86256cedef67b1227d6367b4cdf30c55a74"
other_market = "0x9181874b70fefe3e126b6472c6de647b4dbfa59025ad5dc61be6559532d19e15"

# Create orders 
# Note that this specific set of orders (both long and short buys) can't all be executed together due to subaccount
#   mechanics (see Subaccount Management for more details) and because sells only work with an existing position. 
#   This list of orders is just to demonstrate all types.
response = sdk.injective.create_orders([
  Order.buy_long(some_market, 120, 0.70),
  Order.sell_long(some_market, 100, 0.75),
  Order.buy_short(other_market, 30, 0.90),
  Order.sell_short(other_market, 25, 0.95),
])
Name Type Req? Description
orders [Order] Orders to place; must have at least 1 order
orders[].market_id str Market of the order
orders[].quantity int How many orders to place
orders[].price float At what price to place the order; must be 0 < price < 1
orders[].subaccount_index int 0 Index of the subaccount to create the order from
orders[].is_post_only bool False If True, this is a post-only order that will only succeed if it enters the orderbook unmatched

Response

print("transaction:", response.transaction)

for order in response.orders:
  print(
    "order:",
    order.market_id,
    "for", order.quantity, "@" order.price,
    "in subaccount", order.subaccount_index,
    "with hash", order.hash,
  )
Name Type Description
transaction str Transaction ID of the order creation
orders List[Order] Orders from input, but with hash defined

Failure Detection

Even though a transaction may succeed, the orders within it may individually fail for a number of reasons. Order failure detection is not handled internally within this SDK. However, there is a way to detect failures.

This operation returns your orders augmented with an order hash field, which can be used to cross-reference order failures when retrieving the transaction.

from itertools import chain

create_orders = sdk.injective.create_orders([
  Order.buy_long(some_market, 120, 0.70),
  Order.sell_short(other_market, 25, 0.95),
])

# store order hashes
order_hashes = { order.hash: order for order in create_orders.orders }

get_transaction = sdk.injective.get_transaction(create_orders.transaction)

# find order failures and causes
order_failures = dict(chain.from_iterable(
  zip(order_failure.hashes, order_failure.flags)
  for order_failure
  in get_transaction.order_failures
))

# get (order, flag) correlated by hash
correlated_failed_orders = [
  (order_hashes[order_hash], flag)
  for order_hash, flag
  in order_failures.items()
  if order_hash in order_hashes
]

for order, flag in correlated_failed_orders:
  print("failed order:", order.market_id, order.quantity, "@", order.price, "flag =", flag)

Injective: Create Wallet

Creates a new wallet with a small aidrop of INJ and USDT from Injective's faucet.

Parameters

response = sdk.injective.create_wallet()
Name Type Req? Description
fund_and_initialize bool True If true, fund the wallet from the testnet faucet and initialize (only functions on testnet)

Response

print("wallet eth address:", response.wallet.ethereum_address)
print("wallet inj address:", response.wallet.injective_address)

print("wallet mnenomic (keep this safe):")
print("\n\t", response.wallet.mnemonic)
print("wallet private key (keep this safe):")
print("\n\t", response.wallet.private_key.to_hex())
Name Type Description
wallet Wallet The created wallet

Injective: Fund External Subaccount

Send funds to a subaccount that is NOT owned by the SDK wallet.

Parameters

external_subaccount = Subaccount.from_injective_address_and_index("inj1fjlfjy5adns4msjqch3vqjhesmwjnu9ep045wz", 1)
response = await sdk.injective.fund_external_subaccount(10, "FRCOIN", external_subaccount)
Name Type Req? Description
amount int Amount to send
denom str Denom identifier
destination_subaccount Subaccount Subaccount to send to; cannot be subaccount 0
source_subaccount_index Optional[int] None If provided, send from SDK wallet's subaccount with this index

Response

print("transaction:", response.transaction)
Name Type Description
transaction str Injective Transaction ID

Injective: Fund External Subaccount

Send funds to a separate Injective wallet. This sends from main bank balance to main bank balance.

Parameters

response = await sdk.injective.fund_external_wallet(10, "FRCOIN", "inj1fjlfjy5adns4msjqch3vqjhesmwjnu9ep045wz")
Name Type Req? Description
amount int Amount to send
denom str Denom identifier
destination_injective_address str Injective account to send to

Response

print("transaction:", response.transaction)
Name Type Description
transaction str Injective Transaction ID

Injective: Fund Subaccount

Send funds from the main bank balance to a subaccount that IS owned by the SDK wallet. Or send funds between subaccounts that ARE BOTH owned by the SDK wallet.

Parameters

destination_subaccount_index = 2
wallet = await sdk.wallet()
destination_subaccount = wallet.subaccount(destination_subaccount_index)

# Equivalent methods of transferring from main bank balance to a subaccount
response = await sdk.injective.fund_subaccount(5, "FRCOIN", destination_subaccount_index=1)
response = await sdk.injective.fund_subaccount(5, "FRCOIN", destination_subaccount=destination_subaccount)

# Equivalent methods of transferring between subaccounts
response = await sdk.injective.fund_subaccount(5, "FRCOIN", source_subaccount_index=1, destination_subaccount_index=destination_subaccount_index)
response = await sdk.injective.fund_subaccount(5, "FRCOIN", source_subaccount_index=1, destination_subaccount=destination_subaccount)
Name Type Req? Description
amount int Amount to send
denom str Denom identifier
destination_subaccount Optional[Subaccount] None Subaccount to send to; cannot be subaccount 0.
destination_subaccount_index Optional[int] None Subaccount to send to; cannot be subaccount 0
source_subaccount_index Optional[int] None If provided, send from SDK wallet's subaccount with this index

destination_subaccount and destination_subaccount_index are mutually exclusive, and at least one must be specified.

Response

print("transaction:", response.transaction)
Name Type Description
transaction str Injective Transaction ID

Injective: Fund Wallet from Faucet

Request a small amount of INJ from the faucet.

Parameters

response = sdk.injective.fund_wallet_from_faucet()

There are no parameters for this operation.

Response

print("message:", response.message)
Name Type Description
message str Message from the faucet

Injective: Get Account Portfolio

Retrieves the account portfolio for the current wallet. For the corresponding Injective API, see Account Portfolio.

Parameters

response = sdk.injective.get_account_portfolio()

There are no parameters for this operation.

Response

print("portfolio bank balances:")
for coins in response.portfolio.bank_balances:
  print("\tcoin:", coins.amount, coins.denom)

print("portfolio subaccount balances:")
for subaccount in response.portfolio.subaccounts:
  print(
    "\tsubaccount:",
    subaccount.subaccount_id,
    subaccount.deposit.available_balance, "/", subaccount.deposit.available_balance,
    subaccount.denom,
  )
Name Type Description
portfolio Portfolio Your account portfolio

Injective: Get Order Books

Gets the order books for specific markets. For the corresponding Injective API, see Order Books v2.

Parameters

market_id = "0xd5e4b12b19ecf176e4e14b42944731c27677819d2ed93be4104ad7025529c7ff"

# Get order books for given market ids
response = sdk.injective.get_order_books([market_id])
Name Type Req? Description
market_ids [str] IDs of markets to look up orders for

Response

print("order_books:")
for market_id, order_book in response.order_books.items():
  print("\tmarket:", market_id)

  for buy in order_book.buys:
    print("\t\tbuy:", buy.quantity, "@", buy.price)

  for sell in order_book.sells:
    print("\t\tsell:", sell.quantity, "@", sell.price)
Name Type Description
order_books Mapping[str, DerivativeLimitOrderbookV2] Order books keyed by market ID

Injective: Get Orders

Gets open orders. For the corresponding Injective API, see Orders History.

Parameters

# Get all my orders
response = sdk.injective.get_orders(mine=True)
Name Type Req? Description
mine bool None Only return your orders
market_ids [str] None Only return orders with this market ID
subaccount_id str None Only return orders from this subaccount*
subaccount Subaccount None Only return orders from this subaccount.
subaccount_index int None Only return orders from this subaccount index of your wallet. Sets mine=True
direction "buy", "sell" None Only return orders with this direction
state "booked", "partial_filled", "filled", "canceled" None Only return orders in this state
is_conditional bool None Only return orders that are/are not conditional
order_types [str] None Only return orders with these order types
execution_types ["limit", "market"] None Only return orders with these execution types
start_time datetime None Only return orders starting on or after this time
end_time datetime None Only return orders ending on or before this time

*Only one of subaccount_id, subaccount, or subaccount_index may be provided.

Response

print("orders:")
for order_history in response.orders:
  print(
    "order:",
    order_history.order_type,
    order_history.order.quantity, "@", order_history.order.trigger_price,
    order_history.order.order_hash,
  )
Name Type Description
orders [OrderHistory] Orders

Injective: Get Positions

Gets open positions. For the corresponding Injective API, see Positions.

Parameters

market_id = "0x90e662193fa29a3a7e6c07be4407c94833e762d9ee82136a2cc712d6b87d7de3"

# Get all buy positions
response = sdk.injective.get_positions(
  [market_id],
  direction="buy",
)
Name Type Req? Description
mine bool ? False Only find positions for this wallet
subaccount Subaccount None Only return orders from this subaccount.
subaccount_index int None Only return orders from this subaccount index of your wallet. Sets mine=True
market_ids [str] ? None IDs of markets to look up positions for
direction "buy", "sell" None Only find positions with this direction
start_time datetime None Only find positions executing on or after this time
end_time datetime None Only find positions executing on or before this time

Response

print("positions:", response.positions)
Name Type Description
positions [DerivativePosition] Positions for the given markets

Injective: Get Trades

Get trades (i.e. matched orders). For the corresponding Injective API, see Trades.

Parameters

market_id = "0x90e662193fa29a3a7e6c07be4407c94833e762d9ee82136a2cc712d6b87d7de3"

# Get all trades on the maker side
response = sdk.injective.get_trades(
  [market_id],
  side="maker",
)
Name Type Req? Description
market_ids [str] IDs of markets to look up trades for
mine bool False Only find trades for this wallet
subaccount Subaccount None Only return orders from this subaccount.
subaccount_index int None Only return orders from this subaccount index of your wallet. Sets mine=True
subaccounts [Subaccount] None Only return orders from these subaccounts.
subaccount_indexes [int] None Only return orders from these subaccount indexes of your wallet. Sets mine=True
direction "buy", "sell" None Only find trades with this direction
side "maker", "taker" None Only find trades with this side
start_time datetime None Only find trades executing on or after this time
end_time datetime None Only find trades executing on or before this time

Response

print("trades:", response.trades)
Name Type Description
trades [DerivativeTrade] Trades for the given markets

Injective: Get Transaction

Gets open positions. For the corresponding Injective API, see GetTx.

Parameters

transaction_hash = "BAE72A64BE091B323F508F1887FAF4FA94C0EFE9348831C07DBB078CFC71E16A"

# Get all buy positions
response = sdk.injective.get_transaction(transaction_hash)
Name Type Req? Description
transaction_hash str Injective transaction hash

Response

print("raw injective response:", response.injective_response)
print("order failures:", response.order_failures)
Name Type Description
injective_response GetTxResponse Raw Injective response
order_failures List[OrderFailure] If order failures are found in the logs, list of those failures
order_failures[].flags List[int] Flags (error codes) of the order failure. See https://api.injective.exchange/#error-codes
order_failures[].hashes List[str] Hashes of the order failure

Injective: Refresh Wallet

Refreshes the wallet.

Transactions require a monotonically increasing sequence ID. This sequence ID is tracked internally in the wallet. If the sequence ID known in the wallet drifts for any reason, refreshing the wallet will update the wallet with the latest sequence ID.

Parameters

None.

response = sdk.injective.refresh_wallet()

Response

print("wallet sequence:", response.wallet.sequence)
Name Type Description
wallet Wallet The internal wallet, same instance as sdk.wallet()

Injective: Stream Markets

Get market updates as they arrive. For the corresponding Injective API, see Stream Markets.

Parameters

async def run():
  market_id = "0x90e662193fa29a3a7e6c07be4407c94833e762d9ee82136a2cc712d6b87d7de3"

  response = async_sdk.injective.stream_markets([market_id])
Name Type Req? Description
market_ids [str] Market IDs to watch for updates

Response

async def run():
  response = ...

  async for market in response.markets:
      print(market)
Name Type Description
markets AsyncIterator[DerivativeMarketInfo] Iterator for market updates

Injective: Stream Orders

Get new orders as they arrive. For the corresponding Injective API, see Stream Order History.

Parameters

async def run():
  market_id = "0x90e662193fa29a3a7e6c07be4407c94833e762d9ee82136a2cc712d6b87d7de3"

  response = async_sdk.injective.stream_orders(market_id)
Name Type Req? Description
market_id str Market ID to watch for orders
mine bool False Only watch for my orders
subaccount Subaccount None Only return orders from this subaccount.
subaccount_index int None Only return orders from this subaccount index of your wallet. Sets mine=True
direction "buy", "sell" None Only watch for orders of this direction
subaccount_id bool False Only watch for my orders from this subaccount
order_types bool False Only watch for orders of this type
state bool False Only watch for orders of this state
execution_types bool False Only watch for orders of this type

Response

async def run():
  response = ...

  async for order_history in response.orders:
    print("order:", order_history.order_type, order_history.order.order_hash)
Name Type Description
orders AsyncIterator[OrderHistory] Iterator for orders

Injective: Stream Positions

Get new positions as they arrive. For the corresponding Injective API, see Stream Positions.

Parameters

async def run():
  response = async_sdk.injective.stream_positions()
Name Type Req? Description
mine bool Only stream positions that are mine
market_ids [str] None Only stream positions for the given markets
subaccount_ids [str] None Only stream positions that are from the given subaccount
subaccounts [Subaccount] None Only return orders from these subaccounts.
subaccount_indexes [int] None Only return orders from these subaccount indexes of your wallet. Sets mine=True

Response

async def run():
  response = ...

  async for position in response.positions:
    print(
      "position:",
      position.ticker,
      position.direction,
      position.quantity, "@", position.entry_price,
    )
Name Type Description
positions AsyncIterator[DerivativePosition] Iterator for positions

Injective: Stream Trades

Get new trades as they arrive. For the corresponding Injective API, see Stream Trades.

Parameters

async def run():
  market_id = "0x90e662193fa29a3a7e6c07be4407c94833e762d9ee82136a2cc712d6b87d7de3"

  response = async_sdk.injective.stream_trades(market_id)
Name Type Req? Description
market_id str Market ID to watch for orders
mine bool False Only watch for my orders
subaccount Subaccount None Only return orders from this subaccount.
subaccount_index int None Only return orders from this subaccount index of your wallet. Sets mine=True
subaccounts [Subaccount] None Only return orders from these subaccounts.
subaccount_indexes [int] None Only return orders from these subaccount indexes of your wallet. Sets mine=True
direction "buy", "sell" None Only watch for orders of this direction
side "maker", "taker" None Only watch for orders of this side

Response

async def run():
  response = async_sdk.injective.stream_trades(market_id)

  async for trade in response.trades:
    print("trade:", trade.operation_type, trade.trade.order_hash)
Name Type Description
trades AsyncIterator[DerivativeTrade] Iterator for trades

Injective: Withdraw from Subaccount

Withdraw funds from a subaccount that IS owned by the SDK wallet to the main bank balance.

Parameters

response = await sdk.injective.withdraw_from_subaccount(10, "FRCOIN", subaccount_index=1)
Name Type Req? Description
amount int Amount to send
denom str Denom identifier
subaccount_index int Withdraw from SDK wallet's subaccount with this index

Response

print("transaction:", response.transaction)
Name Type Description
transaction str Injective Transaction ID

Utilities

Currency

Injective operates on raw coin quantities instead of decimal values. For example, 8 FRCOIN (as well as USDC and USDT) would be represented as "8000000". This is because FRCOIN's denomination is to 6 decimal places, and 8 shifted by 6 decimal places is 8,000,000.

Each denomination has its own fractional resolution, and all are mapped in Injective's SDK. See:

Also, denominations are usually returned as their peggy identity. These identities aren't easily human-interpretable and vary between devnet, testnet, and mainnet.

To make working with Injective's units easier, this SDK includes methods to get a unified representation of Currency without needing to know denomination details such as how many digits of precision are involved.

In Currency terms, quantity means the Injective-side raw (decimal-less) value and value means the human-friendly value (with decimals).

From Value

Factory for Currency, given the Injective-side quantity and denomination.

Example

currency = sdk.utilities.currency_from_quantity(420_690_000, "FRCOIN")
print(
  "currency:",
  "[", currency.value, currency.denom.name, "]",
  "is represented as",
  "[", currency.quantity, currency.denom.peggy, "]",
  "in injective",
)

Parameters

Name Type Req? Description
quantity int, str Injective-side coin quantity
denom_name str Name of denomination (human-readable name or peggy identity)

Returns

A currency object.

From Quantity

Factory for Currency, given the human-readable quantity and denomination.

Example

currency = sdk.utilities.currency_from_value(420.69, "FRCOIN")
print(
  "currency:",
  "[", currency.value, currency.denom.name, "]",
  "is represented as",
  "[", currency.quantity, currency.denom.peggy, "]",
  "in injective",
)

Parameters

Name Type Req? Description
value int, str Human-readable coin value
denom_name str Name of denomination (human-readable name or peggy identity)

Returns

A currency object.