Documentation Index Fetch the complete documentation index at: https://docs.injective.network/llms.txt
Use this file to discover all available pages before exploring further.
戦略開発ガイド
設定ガイド
Injective Traderは、動作、コンポーネント、戦略パラメーターを定義するためにYAML設定ファイルを使用します。
特に注目すべき重要な設定セクションは以下の通りです:
LogLevel
Components セクション内の Initializer における Network と MarketTickers
Strategies セクション
設定構造の詳細な内訳は以下の通りです:
トップレベルパラメーター
Exchange : Helix # Trading exchange to use
LogLevel : INFO # Logging level (DEBUG, INFO, WARNING, ERROR)
Componentsセクション
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 : ほとんどのユーザーは、Network の設定と、リッスンしたいすべてのマーケットを MarketTickers に含めることだけを行えば十分です。
これらの高度なコンポーネント設定を変更する必要はありません。デフォルト値はほとんどのユースケースで適切に機能します。
Strategiesセクション
Strategies セクションは各トレーディング戦略を定義します:
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
必須の戦略パラメーター:
Class: Pythonクラス名と正確に一致する必要があります
MarketIds: この戦略でトレードするマーケットIDのリスト(hexフォーマットを使用)
AccountAddresses: この戦略でトレーディングに使用するアカウントのリスト
TradingAccount: 注文執行に使用するアカウント(AccountAddresses に含まれている必要があります)[詳細は Trading Mode Configuration を参照]
推奨パラメーター:
CIDPrefix: client order IDのプレフィックス(自分の注文を識別するのに役立ちます)
Name: ログとモニタリング用の人間可読な名前
カスタムパラメーター:
戦略が必要とする任意のカスタムパラメーターを追加できます
戦略名の下にあるすべてのパラメーターは self.config で利用可能になります
関連パラメーターは明確化のために Parameters セクションでグループ化してください
Trading Modeの設定
フレームワークは2つのトレーディングモードをサポートします:
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 : direct executionのための TradingAccount か、authorizationモードのための Granter および Grantees のいずれかを指定する必要があります。
フレームワークは初期化時にこの要件を強制します。
RetryConfigセクション
RetryConfig セクションは、ネットワーク操作のリトライ動作を制御します:
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には妥当なデフォルトがあり、特定の接続性の問題に遭遇しない限り、通常はカスタマイズの必要はありません。
全体構造を理解したところで、いよいよカスタム戦略を開発する準備ができました!
戦略開発ガイド
Injective Traderの戦略は、Strategy ベースクラスをベースとする一貫した構造に従います。このセクションでは、効果的な戦略を構築する方法を説明します。
Strategyクラスの構造
戦略クラスはベースの Strategy クラスを継承します:
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コンストラクタ(__init__)
戦略クラスには、親クラスのコンストラクタを呼び出すコンストラクタを含めることができます:
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)
ベースクラスのコンストラクタは以下を処理します:
パラメーターのバリデーションと抽出
標準のメトリクスとハンドラのセットアップ(自分のハンドラを書く方法については [block link] を参照)
状態トラッキングコンテナの初期化
トレーディングモード(directまたはauthz)のセットアップ
Important : __init__ メソッドはマーケットデータやアカウント情報にアクセスできません。
これらのリソースを必要とする操作には on_initialize を使用してください。
ベースの __init__ によって提供される利用可能なプロパティは以下の通りです:
Property Description Source Required self.nameStrategy name used in log From config Name YES self.loggerLogger instance For strategy-specific logging YES self.configComplete strategy configuration Corresponding strategy subsection under Strategies section in config YES self.market_idsList of market IDs interested in this strategy From config MarketIds YES self.account_addressesList of account addresses interested in this strategy From config AccountAddresses YES self.subaccount_idsList of subaccount IDs From config SubaccountIds NO self.marketsDictionary of market_id → Market objects Populated during initialization NO self.accountsDictionary of account_address → Account objects Populated during initialization NO self.trading_mode”direct” or “authz” Based on config YES self.fee_recipientFee recipient address From config FeeRecipient NO self.cid_prefixClient order ID prefix From config CIDPrefix NO self.metricsPerformance tracking For recording metrics and alerts YES [DEFAULT] self.handlersEvent handlers dictionary UpdateType → Handler objects YES [DEFAULT]
初期化メソッド(on_initialize)
on_initialize メソッドは、フレームワーク起動時にマーケットとアカウントがロードされた後に一度だけ呼び出されます。
目的 : 戦略の状態とパラメーターを初期化する パラメーター :
accounts: account_address → Account オブジェクトの辞書
markets: market_id → Market オブジェクトの辞書
戻り値 : [Optional] 初期注文(あれば)を含む StrategyResult
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
このメソッドは戦略初期化シーケンスの一部です:
フレームワークがこの戦略で必要なマーケットとアカウントをロードする
ロードされたデータを引数として on_initialize メソッドが呼び出される
返された注文があれば即座に提出される
戦略がrunning stateへ移行する
Tip : マーケットやアカウントデータを必要とするパラメーター初期化、および戦略に必要な初期注文の発注には on_initialize を使用してください。
Account と Market のデータ構造については、以下を参照してください。
戦略ロジック(_execute_strategy)メソッド
_execute_strategy メソッドは「Strategy Execution(execute)Method」の一部です。ベースクラスの execute メソッドが完全な実行フローを処理します:
初期化チェック : 必要に応じて戦略を初期化
状態更新 : 戦略のアカウントおよびマーケット参照を更新
データ処理 : 適切なハンドラを通じて生の更新データを処理
戦略実行 : 処理済みデータを使って _execute_strategy メソッドを呼び出し
注文の補完 : 注文にデフォルト値(fee recipient、client ID)を追加
このメソッドをオーバーライドする必要はほとんどありません。代わりに、カスタムトレーディングロジックが入る _execute_strategy の実装に集中してください:
目的 : マーケットデータを分析し、トレーディングシグナルを生成する パラメーター :
戻り値 : 注文/キャンセルを含む StrategyResult または 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
_execute_strategy では以下のことができます:
特定のイベントをハンドルするためにupdate typeでフィルタリング
現在のマーケットデータとアカウント状態にアクセス
注文発注前に既存ポジションをチェック
マーケット条件に基づくカスタムトレーディングロジックを実装
新規注文を作成し、既存注文をキャンセル
derivativeマーケットでのポジションマージンを更新
モニタリングおよびデバッグのために戦略の意思決定をログ出力
フレームワークは、返された StrategyResult に基づき、トランザクション作成、シミュレーション、ブロードキャストなどの執行詳細を処理します。
ベストプラクティス
すべてのパラメーターを on_initialize で初期化する
パラメーターを self.config から取得
不足パラメーターに対するデフォルト値を設定
内部状態変数を初期化
update typeをフィルタリングする
戦略が気にする update type のみを処理
常に processed_data 内の必須フィールドをチェック
マーケットデータをバリデートする
bid/askが存在することを使用前に確認
ポジションが存在することを意思決定前に検証
マーケット制約を尊重する
価格と数量をマーケットのtick sizeに丸める
最低注文サイズおよびnotional要件をチェック
トレーディングアカウントを適切にハンドルする
トレーディングアカウントがAccountAddressesに含まれることを確認
注文に対して正しいsubaccount_idを指定
適切なロギングを実装する
戦略の意思決定および重要なイベントをログ出力
適切なログレベル(info、warning、error)を使用
カスタムパラメーターを設定する
戦略固有の値には Parameters セクションを使用
期待されるパラメーターをドキュメント化
このガイドは、フレームワークで効果的なトレーディング戦略を作成・設定するための強固な基礎を提供します。
カスタムハンドラ
フレームワークは、戦略にデータを渡す前に、特殊化されたハンドラを通じて更新を処理します。データ処理をより詳細に制御するために、カスタムハンドラを作成できます。
Handlerベースクラス
すべてのハンドラは UpdateHandler ベースクラスから継承します:
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
カスタムハンドラの作成
カスタムハンドラを作成するには:
適切なハンドラベースクラスから継承する
_process_update メソッドをオーバーライドする
戦略のコンストラクタでハンドラを登録する
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
カスタムハンドラの登録
戦略のコンストラクタでカスタムハンドラを登録します:
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
)
利用可能なハンドラタイプ
フレームワークは、拡張可能な以下のハンドラタイプを提供します:
Handler Class Update Type Purpose 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
主要データ構造
Update TypeとそれぞれのデータFields
フレームワークは、戦略が反応できる以下の主要なイベントタイプを処理します:
Update Type Description Key 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
戦略がアクションを取る決定をするとき、以下を含む StrategyResult オブジェクトを返します:
Field Description Content 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
Property Type Description Note 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 price Optional quote_oracle_priceDecimalQuote token oracle price Optional oracle_timestampintOracle price timestamp Optional mark_priceDecimalMark price for derivative Optional
Orderbook
Property Type Description Note sequencestrOrderbook sequence number bidsList[L2PriceLevel]List of bid price levels L2PriceLevel : price and quantityasksList[L2PriceLevel]List of ask price levels L2PriceLevel : price and quantitytobTupleTop of book (bid, ask) prices is_healthboolIndicates if orderbook is in healthy state False when sequence is 0
Account
Property Type Description Note private_keyPrivateKeyInjective private key Not directly accessible public_keyPublicKeyCorresponding public key addressAddressAccount address object Contains sequence information account_addressstrBech32 address string Format: “inj1…” bank_balancesDict[str, BankBalance]Token balances in account Key: denom balancesDict[str, Balance]Alternative balance representation Key: denom subaccountsDict[str, SubAccount]Subaccounts owned by this account Key: subaccount_id sequenceintTransaction sequence number Property that accesses address.sequence
BankBalance
Property Type Description Note denomstrToken denomination e.g., “inj”, “peggy0x…” amountDecimalToken amount Human-readable format
Balance
Property Type Description Note denomstrToken denomination e.g., “inj”, “peggy0x…” totalDecimalTotal balance availableDecimalAvailable balance Total minus locked amounts
SubAccount
Property Type Description Note subaccount_idstrUnique subaccount identifier Format: “0x…” portfolioDict[str, Deposit]Token deposits in subaccount Key: denom positionsDict[str, Position]Trading positions Key: market_id open_bid_ordersDict[str, Dict[str, Order]]Open buy orders market_id -> {order_hash: Order}open_ask_ordersDict[str, Dict[str, Order]]Open sell orders market_id -> {order_hash: Order}traded_bid_ordersDict[str, Dict[str, Order]]Filled buy orders market_id -> {order_hash: Order}traded_ask_ordersDict[str, Dict[str, Order]]Filled sell orders market_id -> {order_hash: Order}
Deposit
Property Type Description Note denomstrToken denomination e.g., “inj”, “peggy0x…” total_balanceDecimalTotal deposit amount available_balanceDecimalAvailable deposit amount Total minus margins and locked
Position
Property Type Description Note 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/loss Default: 0 total_volumeDecimalTotal trading volume Default: 0 trades_countintNumber of trades Default: 0 mark_priceDecimalCurrent market price Optional liquidation_priceDecimalLiquidation threshold Optional margin_ratioDecimalCurrent margin ratio Optional
Order
Property Type Description Note market_idstrMarket identifier subaccount_idstrSubaccount identifier order_sideSideBuy or sell Side.BUY or Side.SELL priceDecimalOrder price quantityDecimalOrder quantity order_hashstrUnique order identifier Optional, set by chain fillableDecimalRemaining unfilled quantity Optional filledDecimalFilled quantity Optional statusOrderStatusOrder status BOOKED, PARTIAL_FILLED, etc. order_typestrOrder type Default: “LIMIT” marginDecimalMargin amount (derivatives) Optional leverageDecimalLeverage multiple Optional trigger_priceDecimalFor conditional orders Optional market_typeMarketTypeSPOT, DERIVATIVE, BINARY Optional fee_recipientstrFee recipient address Default: "" cidstrClient order ID Optional created_atdatetimeCreation timestamp Optional updated_atdatetimeLast update timestamp Optional position_deltaDictPosition change data Optional for derivatives payoutDecimalExpected payout Optional for derivatives tx_hashstrTransaction hash Optional error_codestrError code if failed Optional error_messagestrError details Optional
OrderStatus (Enum)
Value Description BOOKEDOrder accepted and active PARTIAL_FILLEDPartially filled FILLEDCompletely filled CANCELLEDCancelled by user EXPIREDExpired (time-based)
Side (Enum)
Value Description BUYBuy order SELLSell order
MarketType (Enum)
Value Description SPOTSpot market DERIVATIVEDerivatives market BINARYBinary options market