Skip to main content

Strategy Development Guide

Configuration Guide

The Injective Trader uses a YAML configuration file to define behavior, components, and strategy parameters. The most important configuration sections to focus on are:
  • LogLevel
  • Network and MarketTickers in the Initializer under the Components section
  • Strategies section
Here’s a detailed breakdown of the configuration structure:

Top-Level Parameters

Exchange: Helix                # Trading exchange to use
LogLevel: INFO                 # Logging level (DEBUG, INFO, WARNING, ERROR)

Components Section

The Components section configures framework components:
Components:
  # Chain initialization and market setup
  Initializer:
    Network: mainnet           # Network to connect to (mainnet or testnet)
    MarketTickers:             # Market tickers to track (will be converted to IDs)
      - INJ/USDT PERP
      - ETH/USDT
    BotName: MyBot

  # Chain listening configuration
  ChainListener:
    ReconnectionDelay: 5       # Seconds to wait before reconnection attempts
    LargeGapThreshold: 50      # Sequence gap threshold for orderbook snapshot requests

  # Transaction broadcasting configuration
  MessageBroadcaster:
    ErrorCodesJson: config/error_codes.json   # Error code lookup for tx validation
    GranteePool:               # For authz transaction mode
      MaxPendingTxs: 5         # Maximum pending transactions per grantee
      ErrorThreshold: 3        # Consecutive errors before blocking a grantee
      BlockDuration: 300       # Seconds to block a grantee after errors
      RotationInterval: 1      # Seconds between grantee rotations
    RefreshInterval: 300       # Seconds between grant refresh checks
    Batch:                     # Transaction batching settings
      MaxBatchSize: 15         # Maximum messages per batch
      MinBatchSize: 3          # Minimum messages to trigger immediate send
      MaxGasLimit: 5000000     # Maximum gas per batch
      MaxBatchDelay: 0.5       # Maximum seconds to wait for batch completion
Note: Most users only need to take care of Network and include all the markets that they want to listen to in MarketTickers. They won’t need to modify these advanced component settings. The default values work well for most use cases.

Strategies Section

The Strategies section defines each trading strategy:
Strategies:
  SimpleStrategy:                                    # Strategy identifier (your choice)
    # Required parameters
    Name: "SimpleStrategy"                           # Strategy name (used in logs)
    Class: "SimpleStrategy"                          # [REQUIRED] Python class name to instantiate
    MarketIds:                                       # [REQUIRED] Markets to trade on
      - "0x9b9980167ecc3645ff1a5517886652d94a0825e54a77d2057cbbe3ebee015963"  # INJ/USDT PERP
    AccountAddresses:                                # [REQUIRED] Accounts to use
      - "inj1youractualaccount..."                   # (Must match private key in env)
    TradingAccount: "inj1youractualaccount..."       # [REQUIRED] Account for placing orders (Must match private key in env)

    # Optional parameters
    FeeRecipient: "inj1feerecipient..."   # Address to receive trading fees (if applicable)
    CIDPrefix: "simple_strat"              # Prefix for client order IDs
    SubaccountIds: ["0x123..."]            # Specific subaccounts to use (otherwise all available)

    # Risk management configuration [Optional]
    # You don't have to include them if you don't have your specific risk model
    Risk: "BasicRiskModel"                 # Risk model to apply (if using risk management)
    RiskConfig:                            # Risk thresholds
      DrawdownWarning: 0.1                 # 10% drawdown warning threshold
      DrawdownCritical: 0.2                # 20% drawdown critical threshold
      MarginWarning: 0.7                   # 70% margin usage warning
      MarginCritical: 0.8                  # 80% margin usage critical
Required Strategy Parameters:
  • Class: Must exactly match your Python class name
  • MarketIds: List of market IDs to trade on in this strategy (use hex format)
  • AccountAddresses: List of accounts to use for trading in this strategy
  • TradingAccount: Account used for order execution (must be in AccountAddresses) [See more details on Trading Mode Configuration ]
Recommended Parameters:
  • CIDPrefix: Prefix for client order IDs (helps identify your orders)
  • Name: Human-readable name for logs and monitoring
Custom Parameters:
  • You can add any custom parameters your strategy needs
  • All parameters under your strategy name will be available in self.config
  • Group related parameters under the Parameters section for clarity

Trading Mode Configuration

The framework supports two trading modes:

Direct Execution Mode

Strategies:
  SimpleStrategy:
    # Other parameters...
    TradingAccount: "inj1youraccount..."   # Account that will sign and broadcast transactions

Authorization (Authz) Mode

Strategies:
  SimpleStrategy:
    # Other parameters...
    Granter: "inj1granteraccount..."   # Account granting permission to execute trades
    Grantees:                          # Accounts that can execute trades on behalf of granter
      - "inj1grantee1..."
      - "inj1grantee2..."
Note: You must specify either TradingAccount for direct execution OR Granter and Grantees for authorization mode. The framework enforces this requirement during initialization.

RetryConfig Section

The RetryConfig section controls retry behavior for network operations:
RetryConfig:
  # Global retry settings
  DefaultRetry:
    max_attempts: 3            # Maximum retry attempts
    base_delay: 1.0            # Base delay between retries (seconds)
    max_delay: 32.0            # Maximum delay cap (seconds)
    jitter: true               # Add randomness to delay
    timeout: 30.0              # Operation timeout (seconds)
    error_threshold: 10        # Errors before circuit breaking
    error_window: 60           # Error counting window (seconds)

  # Component-specific retry settings (override defaults)
  ChainListener:
    max_attempts: 5            # More retries for chain listener
    base_delay: 2.0
    max_delay: 45.0
  MessageBroadcaster:
    max_attempts: 3
    base_delay: 1.0
    max_delay: 30.0
Note: RetryConfig has sensible defaults and typically doesn’t need customization unless you’re experiencing specific connectivity issues.

Now that we understand the overall structure, we are ready to develop custom ones!

Strategy Development Guide

Strategies in the Injective Trader follow a consistent structure based on the Strategy base class. This section explains how to build effective strategies.

Strategy Class Structure

Your strategy class inherits from the base Strategy class:
from src.core.strategy import Strategy, StrategyResult
from src.utils.enums import UpdateType, Side

class SimpleStrategy(Strategy):
    def __init__(self, logger, config):
		"""
        Initialize strategy with logger and configuration.

        Args:
            logger: Logger instance for strategy logging
            config: Strategy configuration dictionary
                Required keys:
                - MarketIds: List of market IDs to trade
                - AccountAddresses: List of account addresses to use
                Optional keys:
                - Name: Strategy name
                - Parameters: Strategy-specific parameters
                - RiskConfig: Risk management parameters
        """
        super().__init__(logger, config)

    def on_initialize(self, accounts, markets):
        """
        Initialize strategy-specific state and parameters.
        Called once before strategy starts processing updates.

        Args:
            accounts: Dictionary of account_address -> Account
            markets: Dictionary of market_id -> Market
        """
        pass

    async def _execute_strategy(self, update_type, processed_data):
        """
        Strategy-specific execution logic.

       Args:
          update_type: Type of update being processed
          processed_data: Update-specific data dictionary after handler processing
              Common fields:
              - market_id: Market identifier
              - account_address: Account address (for account updates)
              - subaccount_id: Subaccount identifier (for position/trade updates)

        Returns:
            StrategyResult:
            - orders
            - cancellations
            - margin updates
        """
        pass

Strategy Constructor (__init__)

Your strategy class can include a constructor that calls the parent class constructor:
def __init__(self, logger, config):
    super().__init__(logger, config)

    # Custom initialization that doesn't require markets or accounts
    self.custom_parameter = 42

    # Optional: Custom handler overrides
    self.handlers[UpdateType.OnOrderbook] = MyCustomOrderbookHandler(
        self.logger, self.config, self.metrics
    )

    # Optional: Custom performance metrics overrides
    self.my_metrics = MyCustomPerformanceMetrics(self.logger)
The base class constructor handles:
  1. Parameter validation and extraction
  2. Setting up standard metrics and handlers (See [block link] for more information on writing your own handlers)
  3. Initializing state tracking containers
  4. Setting up trading mode (direct or authz)
Important: The __init__ method cannot access market data or account information. Use on_initialize for operations requiring those resources.
Available properties provided by base __init__ are:
PropertyDescriptionSourceRequired
self.nameStrategy name used in logFrom config NameYES
self.loggerLogger instanceFor strategy-specific loggingYES
self.configComplete strategy configurationCorresponding strategy subsection under Strategies section in configYES
self.market_idsList of market IDs interested in this strategyFrom config MarketIdsYES
self.account_addressesList of account addresses interested in this strategyFrom config AccountAddressesYES
self.subaccount_idsList of subaccount IDsFrom config SubaccountIdsNO
self.marketsDictionary of market_id → Market objectsPopulated during initializationNO
self.accountsDictionary of account_address → Account objectsPopulated during initializationNO
self.trading_mode”direct” or “authz”Based on configYES
self.fee_recipientFee recipient addressFrom config FeeRecipientNO
self.cid_prefixClient order ID prefixFrom config CIDPrefixNO
self.metricsPerformance trackingFor recording metrics and alertsYES [DEFAULT]
self.handlersEvent handlers dictionaryUpdateType → Handler objectsYES [DEFAULT]

Initialization Method (on_initialize)

The on_initialize method is called once during framework startup, after markets and accounts are loaded. Purpose: Initialize strategy state and parameters Parameters:
  • accounts: Dictionary of account_address → Account objects
  • markets: Dictionary of market_id → Market objects
Returns: [Optional] StrategyResult with initial orders (if any)
def on_initialize(self, accounts, markets):
    # Now you have access to all market and account data

    # Example: Access market metadata
    for market_id in self.market_ids:
        market = markets[market_id]
        self.logger.info(f"Market {market_id} tick sizes: "
                         f"price={market.min_price_tick}, "
                         f"quantity={market.min_quantity_tick}")

    # Example: Initialize parameters that need market info
    self.avg_prices = {
        market_id: markets[market_id].orderbook.tob()[0]
        for market_id in self.market_ids
        if markets[market_id].orderbook.tob()[0]
    }

    # Example: Place initial orders
    if self.config.get("PlaceInitialOrders", False):
        result = StrategyResult()
        # Add initial orders...
        return result

    return None  # No initial orders
This method is part of the strategy initialization sequence:
  1. Framework loads markets and accounts required by this strategy
  2. Your on_initialize method is called with loaded data
  3. Any returned orders are immediately submitted
  4. The strategy moves to running state
Tip: Use on_initialize for parameter initialization that requires market or account data, and to place any initial orders needed for your strategy. For data structure information on Account and Market, see below.

Strategy Logic (_execute_strategy ) Method

The _execute_strategy method is a part of “Strategy Execution (execute) Method”. The base class execute method handles the complete execution flow:
  1. Initialization check: Initializes the strategy if needed
  2. State update: Updates the strategy’s account and market references
  3. Data processing: Processes raw update data through the appropriate handler
  4. Strategy execution: Calls your _execute_strategy method with processed data
  5. Order enrichment: Adds default values to orders (fee recipient, client ID)
You rarely need to override this method. Instead, focus on implementing _execute_strategy where your custom trading logic goes: Purpose: Analyze market data and generate trading signals Parameters: Returns: StrategyResult with orders/cancellations or None
async def _execute_strategy(self, update_type, processed_data):
    # Only respond to orderbook updates
    if update_type != UpdateType.OnOrderbook:
        return None

    # Get market data
    market_id = processed_data["market_id"]
    market = self.markets[market_id]

    # Get current prices
    bid, ask = market.orderbook.tob()
    if not bid or not ask:
        self.logger.warning(f"Incomplete orderbook for {market_id}")
        return None

    # Implement your strategy logic
    spread = (ask - bid) / bid
    if spread < self.min_spread_threshold:
        self.logger.info(f"Spread too narrow: {spread:.2%}")
        return None

    # Get subaccount for orders
    subaccount_id = self.config.get("SubaccountIds", [""])[0]
    if not subaccount_id:
        return None

    # Check current position to avoid exceeding limits
    position = self.get_position(subaccount_id, market_id)
    current_size = Decimal("0")
    if position:
        current_size = position.get("quantity", Decimal("0"))

    # Determine order parameters
    result = StrategyResult()

    # Create a new order
    if current_size < self.max_position:
        buy_order = Order(
            market_id=market_id,
            subaccount_id=subaccount_id,
            order_side=Side.BUY,
            price=bid,
            quantity=self.order_size,
            market_type=market.market_type # Required field as of v0.5.1
        )
        result.orders.append(buy_order)
        self.logger.info(f"Creating BUY order at {bid}: {self.order_size}")

    # Cancel existing orders if needed
    for order_hash in self.active_orders:
        result.cancellations.append({
            "market_id": market_id,
            "subaccount_id": subaccount_id,
            "order_hash": order_hash
        })

    return result
In _execute_strategy you can:
  • Filter by update type to handle specific events
  • Access current market data and account state
  • Check existing positions before placing orders
  • Implement custom trading logic based on market conditions
  • Create new orders and cancel existing ones
  • Update position margins for derivative markets
  • Log strategy decisions for monitoring and debugging
The framework will handle the execution details like transaction creation, simulation, and broadcasting based on your returned StrategyResult.

Best Practices

  1. Initialize all parameters in on_initialize
    • Get parameters from self.config
    • Set default values for missing parameters
    • Initialize internal state variables
  2. Filter update types
    • Only process update types your strategy cares about
    • Always check for required fields in processed_data
  3. Validate market data
    • Check if bid/ask exists before using
    • Verify position exists before making decisions based on it
  4. Respect market constraints
    • Round prices and quantities to market tick sizes
    • Check minimum order size and notional requirements
  5. Handle trading account properly
    • Ensure trading account is in AccountAddresses
    • Specify correct subaccount_id for orders
  6. Implement proper logging
    • Log strategy decisions and important events
    • Use appropriate log levels (info, warning, error)
  7. Set custom parameters
    • Use the Parameters section for strategy-specific values
    • Document expected parameters
This guide should give you a solid foundation for creating and configuring effective trading strategies with the framework.

Custom Handlers

The framework processes updates through specialized handlers before passing the data to your strategy. You can create custom handlers for more control over data processing.

Handler Base Class

All handlers inherit from the UpdateHandler base class:
class UpdateHandler(ABC):
    def __init__(self, logger, config, metrics):
        self.logger = logger
        self.config = config
        self.metrics = metrics

    async def process(self, **update_data) -> Dict:
        """Process update data and return processed result"""
        try:
            return await self._process_update(**update_data)
        except Exception as e:
            self.logger.error(f"Error processing update: {e}")
            return None

    @abstractmethod
    async def _process_update(self, **kwargs) -> Dict:
        """Process specific update type. To be implemented by subclasses."""
        pass

Creating a Custom Handler

To create a custom handler:
  1. Inherit from the appropriate handler base class
  2. Override the _process_update method
  3. Register your handler in your strategy’s constructor
from src.core.handlers import OrderbookHandler

class MyCustomOrderbookHandler(OrderbookHandler):
    async def _process_update(self, **update_data):
        # Get basic processed data
        processed_data = await super()._process_update(**update_data)
        if not processed_data:
            return None

        # Add custom metrics
        market = update_data.get("market")
        bid, ask = market.orderbook.tob()

        if bid and ask:
            # Calculate custom metrics
            spread = ask - bid
            spread_pct = (ask - bid) / bid

            # Add to processed data
            processed_data["spread"] = spread
            processed_data["spread_pct"] = spread_pct

            # Record in metrics system
            self.metrics.add_custom_metric(f"{market.market_id}_spread", spread_pct)

        return processed_data

Registering Custom Handlers

Register your custom handlers in your strategy constructor:
def __init__(self, logger, config):
    super().__init__(logger, config)

    # Replace default handlers with custom implementations
    self.handlers[UpdateType.OnOrderbook] = MyCustomOrderbookHandler(
        self.logger, self.config, self.metrics
    )
    self.handlers[UpdateType.OnPosition] = MyCustomPositionHandler(
        self.logger, self.config, self.metrics
    )

Available Handler Types

The framework provides these handler types that you can extend:
Handler ClassUpdate TypePurpose
OrderbookHandlerUpdateType.OnOrderbookProcess orderbook updates
OracleHandlerUpdateType.OnOraclePriceProcess oracle price updates
PositionHandlerUpdateType.OnPositionProcess position updates
BalanceHandlerUpdateType.OnBankBalanceProcess balance updates
DepositHandlerUpdateType.OnDepositProcess deposit updates
TradeHandlerUpdateType.OnSpotTradeProcess trade execution
OrderHandlerUpdateType.OnSpotOrderProcess order updates

Key Data Structure

Update Types and Corresponding Data Fields

The framework processes these main event types that your strategy can react to:
Update TypeDescriptionKey Data Fields
UpdateType.OnOrderbookOrderbook updates

- market_id: str
- market: Market object with updated orderbook
- sequence: int
- block_time: Optional[str]

UpdateType.OnOraclePriceOracle price updates

- market_id: str
- symbol: str
- price: Decimal
- timestamp: int

UpdateType.OnBankBalanceAccount balance updates

- account_address: str
- account: Account object
- balance: BankBalance object

UpdateType.OnDepositSubaccount deposit updates

- account_address: str
- subaccount_id: str
- account: Account object
- deposit: Deposit object

UpdateType.OnPositionPosition changes

- market_id: str
- account_address: str
- subaccount_id: str
- account: Account object
- position: Position object

UpdateType.OnSpotTradeSpot trade execution

- market_id: str
- subaccount_id: str
- account: Account object
- trade: Order object representing the trade
- order: Original Order object that was filled

UpdateType.OnDerivativeTradeDerivative trade execution

- market_id: str
- subaccount_id: str
- account: Account object
- trade: Order object representing the trade
- order: Original Order object that was filled

UpdateType.OnSpotOrderSpot order updates

- market_id: str
- subaccount_id: str
- account: Account object
- order: Order object

UpdateType.OnDerivativeOrderDerivative order updates

- market_id: str
- subaccount_id: str
- account: Account object
- order: Order object

Strategy Result

When your strategy decides to take action, return a StrategyResult object with:
FieldDescriptionContent
ordersList of new orders to create[Order(...), Order(...)]
cancellationsList of orders to cancel[{"market_id": "0x123...", "subaccount_id": "0x456...", "order_hash": "0x789..."}]
margin_updatesList of margin adjustments[{"action": "increase", "market_id": "0x123...", "source_subaccount_id": "0x456...", "destination_subaccount_id": "0x456...", "amount": Decimal("50.0")}]
liquidationsList of positions to be liquidated due to insufficient margin[{"market_id": "0x123...", "subaccount_id": "0x456...", "executor_subaccount_id": "0x789...", "price": price, "quantity": quantity, "margin": margin}]

Market

PropertyTypeDescriptionNote
market_idstrUnique market identifier
market_typeMarketType objectMarket type (SPOT, DERIVATIVE or BINARY)Required as of v0.5.1
marketBinaryOptionMarket | DerivativeMarket | SpotMarketMarket object from pyinjective

SpotMarket: https://api.injective.exchange/#chain-exchange-for-spot-spotmarkets
DerivativeMarket: https://api.injective.exchange/#chain-exchange-for-derivatives-derivativemarkets
BinaryOptionMarket: https://api.injective.exchange/#chain-exchange-for-binary-options-binaryoptionsmarkets

orderbookOrderbook objectCurrent market orderbook
oracle_priceDecimalCurrent market oracle price
min_price_tickDecimalMinimum price increment
min_quantity_tickDecimalMinimum quantity increment
base_oracle_priceDecimalBase token oracle priceOptional
quote_oracle_priceDecimalQuote token oracle priceOptional
oracle_timestampintOracle price timestampOptional
mark_priceDecimalMark price for derivativeOptional

Orderbook

PropertyTypeDescriptionNote
sequencestrOrderbook sequence number
bidsList[L2PriceLevel]List of bid price levelsL2PriceLevel : price and quantity
asksList[L2PriceLevel]List of ask price levelsL2PriceLevel : price and quantity
tobTupleTop of book (bid, ask) prices
is_healthboolIndicates if orderbook is in healthy stateFalse when sequence is 0

Account

PropertyTypeDescriptionNote
private_keyPrivateKeyInjective private keyNot directly accessible
public_keyPublicKeyCorresponding public key
addressAddressAccount address objectContains sequence information
account_addressstrBech32 address stringFormat: “inj1…”
bank_balancesDict[str, BankBalance]Token balances in accountKey: denom
balancesDict[str, Balance]Alternative balance representationKey: denom
subaccountsDict[str, SubAccount]Subaccounts owned by this accountKey: subaccount_id
sequenceintTransaction sequence numberProperty that accesses address.sequence

BankBalance

PropertyTypeDescriptionNote
denomstrToken denominatione.g., “inj”, “peggy0x…”
amountDecimalToken amountHuman-readable format

Balance

PropertyTypeDescriptionNote
denomstrToken denominatione.g., “inj”, “peggy0x…”
totalDecimalTotal balance
availableDecimalAvailable balanceTotal minus locked amounts

SubAccount

PropertyTypeDescriptionNote
subaccount_idstrUnique subaccount identifierFormat: “0x…”
portfolioDict[str, Deposit]Token deposits in subaccountKey: denom
positionsDict[str, Position]Trading positionsKey: market_id
open_bid_ordersDict[str, Dict[str, Order]]Open buy ordersmarket_id -> {order_hash: Order}
open_ask_ordersDict[str, Dict[str, Order]]Open sell ordersmarket_id -> {order_hash: Order}
traded_bid_ordersDict[str, Dict[str, Order]]Filled buy ordersmarket_id -> {order_hash: Order}
traded_ask_ordersDict[str, Dict[str, Order]]Filled sell ordersmarket_id -> {order_hash: Order}

Deposit

PropertyTypeDescriptionNote
denomstrToken denominatione.g., “inj”, “peggy0x…”
total_balanceDecimalTotal deposit amount
available_balanceDecimalAvailable deposit amountTotal minus margins and locked

Position

PropertyTypeDescriptionNote
subaccount_idstrOwner subaccount ID
market_idstrMarket identifier
quantityDecimalPosition size
entry_priceDecimalAverage entry price
marginDecimalTotal margin amount
cumulative_funding_entryDecimalCumulative funding at entry
is_longboolLong (true) or short (false)
unrealized_pnlDecimalUnrealized profit/lossDefault: 0
total_volumeDecimalTotal trading volumeDefault: 0
trades_countintNumber of tradesDefault: 0
mark_priceDecimalCurrent market priceOptional
liquidation_priceDecimalLiquidation thresholdOptional
margin_ratioDecimalCurrent margin ratioOptional

Order

PropertyTypeDescriptionNote
market_idstrMarket identifier
subaccount_idstrSubaccount identifier
order_sideSideBuy or sellSide.BUY or Side.SELL
priceDecimalOrder price
quantityDecimalOrder quantity
order_hashstrUnique order identifierOptional, set by chain
fillableDecimalRemaining unfilled quantityOptional
filledDecimalFilled quantityOptional
statusOrderStatusOrder statusBOOKED, PARTIAL_FILLED, etc.
order_typestrOrder typeDefault: “LIMIT”
marginDecimalMargin amount (derivatives)Optional
leverageDecimalLeverage multipleOptional
trigger_priceDecimalFor conditional ordersOptional
market_typeMarketTypeSPOT, DERIVATIVE, BINARYOptional
fee_recipientstrFee recipient addressDefault: ""
cidstrClient order IDOptional
created_atdatetimeCreation timestampOptional
updated_atdatetimeLast update timestampOptional
position_deltaDictPosition change dataOptional for derivatives
payoutDecimalExpected payoutOptional for derivatives
tx_hashstrTransaction hashOptional
error_codestrError code if failedOptional
error_messagestrError detailsOptional

OrderStatus (Enum)

ValueDescription
BOOKEDOrder accepted and active
PARTIAL_FILLEDPartially filled
FILLEDCompletely filled
CANCELLEDCancelled by user
EXPIREDExpired (time-based)

Side (Enum)

ValueDescription
BUYBuy order
SELLSell order

MarketType (Enum)

ValueDescription
SPOTSpot market
DERIVATIVEDerivatives market
BINARYBinary options market