Skip to content

Data Models

Comprehensive data models and type definitions for trading operations, market data, and API responses.

Overview

The models module provides strongly-typed data structures for all SDK operations including trading entities, API responses, configuration objects, and market data structures.

Core Trading Models

Account Information

# Example account usage
async with ProjectX.from_env() as client:
    await client.authenticate()
    account = await client.get_account_info()

    print(f"Account ID: {account.account_id}")
    print(f"Balance: ${account.balance:,.2f}")
    print(f"Available: ${account.available_balance:,.2f}")
    print(f"Margin Used: ${account.margin_used:,.2f}")

Order Models

# Example order models usage
from project_x_py import TradingSuite

async def order_models_example():
    suite = await TradingSuite.create(["MNQ"])
    mnq_context = suite["MNQ"]

    # Place order and get response
    response = await mnq_context.orders.place_limit_order(
        contract_id=mnq_context.instrument_info.id,
        side=0,  # Buy
        size=1,
        limit_price=21000.0
    )

    # Access response fields
    print(f"Order ID: {response.order_id}")
    print(f"Status: {response.status}")
    print(f"Message: {response.message}")

    # Get order details
    order = await mnq_context.orders.get_order(response.order_id)
    print(f"Order Size: {order.size}")
    print(f"Order Price: ${order.price:.2f}")
    print(f"Time in Force: {order.time_in_force}")

    await suite.disconnect()

Position Models

# Example position usage
async def position_models_example():
    suite = await TradingSuite.create(["MNQ"])
    mnq_positions = suite["MNQ"].positions

    position = await mnq_positions.get_position("MNQ")
    if position:
        print(f"Instrument: {position.instrument}")
        print(f"Size: {position.size}")
        print(f"Average Price: ${position.avg_price:.2f}")
        print(f"Unrealized P&L: ${position.unrealized_pnl:.2f}")
        print(f"Market Value: ${position.market_value:.2f}")
        print(f"Open Time: {position.open_time}")

    await suite.disconnect()

Trade Models

# Example trade usage
async def trade_models_example():
    suite = await TradingSuite.create(["MNQ"])

    # Get recent trades
    trades = await suite.client.get_recent_trades(limit=10)

    for trade in trades:
        print(f"Trade ID: {trade.trade_id}")
        print(f"Order ID: {trade.order_id}")
        print(f"Instrument: {trade.instrument}")
        print(f"Side: {trade.side}")
        print(f"Quantity: {trade.quantity}")
        print(f"Price: ${trade.price:.2f}")
        print(f"Commission: ${trade.commission:.2f}")
        print(f"Timestamp: {trade.timestamp}")
        print("---")

    await suite.disconnect()

Market Data Models

Instrument Information

# Example instrument usage
async def instrument_models_example():
    async with ProjectX.from_env() as client:
        await client.authenticate()

        # Get instrument details
        instrument = await client.get_instrument("MNQ")

        print(f"Symbol: {instrument.symbol}")
        print(f"Description: {instrument.description}")
        print(f"Exchange: {instrument.exchange}")
        print(f"Currency: {instrument.currency}")
        print(f"Tick Size: {instrument.tick_size}")
        print(f"Tick Value: ${instrument.tick_value}")
        print(f"Contract Size: {instrument.contract_size}")
        print(f"Margin Requirement: ${instrument.margin_requirement:.2f}")

OHLCV Bar Data

Market data is typically represented as Polars DataFrames with standardized column names:

# Standard OHLCV DataFrame structure
import polars as pl

# Example DataFrame schema
bars_schema = {
    "timestamp": pl.Datetime,
    "open": pl.Float64,
    "high": pl.Float64,
    "low": pl.Float64,
    "close": pl.Float64,
    "volume": pl.Int64
}

# Example usage
async def bar_data_example():
    async with ProjectX.from_env() as client:
        await client.authenticate()

        # Get OHLCV bars (returns Polars DataFrame)
        bars = await client.get_bars("MNQ", days=5, interval=60)

        print(f"Data shape: {bars.shape}")
        print(f"Columns: {bars.columns}")
        print(f"Latest close: ${bars.tail(1)['close'].item():.2f}")

        # Access individual columns
        closes = bars["close"]
        volumes = bars["volume"]
        timestamps = bars["timestamp"]

Quote Data

# Quote data structure
from dataclasses import dataclass
from datetime import datetime
from decimal import Decimal

@dataclass
class Quote:
    """Real-time quote data."""
    instrument: str
    bid: Decimal
    ask: Decimal
    bid_size: int
    ask_size: int
    timestamp: datetime

    @property
    def spread(self) -> Decimal:
        """Calculate bid-ask spread."""
        return self.ask - self.bid

    @property
    def mid_price(self) -> Decimal:
        """Calculate mid-point price."""
        return (self.bid + self.ask) / 2

# Example quote usage
async def quote_example():
    suite = await TradingSuite.create(["MNQ"])
    mnq_data = suite["MNQ"].data

    quote = await mnq_data.get_current_quote()
    print(f"Bid: ${quote.bid:.2f} x {quote.bid_size}")
    print(f"Ask: ${quote.ask:.2f} x {quote.ask_size}")
    print(f"Spread: ${quote.spread:.2f}")
    print(f"Mid: ${quote.mid_price:.2f}")

    await suite.disconnect()

Tick Data

@dataclass
class Tick:
    """Individual tick data."""
    instrument: str
    price: Decimal
    size: int
    timestamp: datetime
    side: str  # "buy", "sell", or "unknown"

# Example tick usage
async def tick_example():
    suite = await TradingSuite.create(["MNQ"])
    mnq_data = suite["MNQ"].data
    await mnq_data.subscribe_to_trades()

    # Wait for tick data
    await asyncio.sleep(30)

    ticks = await mnq_data.get_recent_ticks(count=20)
    for tick in ticks[-5:]:  # Last 5 ticks
        print(f"${tick.price:.2f} x {tick.size} ({tick.side}) @ {tick.timestamp}")

    await suite.disconnect()

Configuration Models

ProjectXConfig

# Example configuration usage
from project_x_py.models import ProjectXConfig

# Create custom configuration
config = ProjectXConfig(
    api_key="your_api_key"  # pragma: allowlist secret,
    username="your_username",
    api_url="https://gateway.projectx.com/api",
    timeout_seconds=60,
    retry_attempts=5,
    rate_limit_calls=100,
    enable_caching=True,
    cache_ttl_seconds=300
)

# Use configuration with client
async with ProjectX(config) as client:
    await client.authenticate()
    # Use client with custom config

Component Configurations

from project_x_py.types import (
    OrderManagerConfig,
    PositionManagerConfig,
    DataManagerConfig,
    OrderbookConfig
)

# Order Manager Configuration
order_config = OrderManagerConfig(
    max_concurrent_orders=10,
    default_timeout=30.0,
    retry_attempts=3,
    enable_order_tracking=True,
    track_performance=True
)

# Position Manager Configuration
position_config = PositionManagerConfig(
    track_unrealized=True,
    calculate_metrics=True,
    update_frequency=1.0,
    enable_trade_journal=True
)

# Data Manager Configuration
data_config = DataManagerConfig(
    max_bars_per_timeframe=1000,
    enable_tick_data=True,
    data_validation=True,
    auto_cleanup=True,
    compression_enabled=True
)

# OrderBook Configuration
orderbook_config = OrderbookConfig(
    max_depth_levels=10,
    enable_order_flow=True,
    track_volume_profile=True,
    enable_spoofing_detection=True
)

Response Models

API Response Types

# Example response handling
async def response_handling_example():
    try:
        async with ProjectX.from_env() as client:
            response = await client.authenticate()

            # Check response status
            if response.success:
                print(f"Authentication successful: {response.message}")
                print(f"User ID: {response.user_id}")
                print(f"Token expires: {response.expires_at}")
            else:
                print(f"Authentication failed: {response.error}")

    except ProjectXAuthenticationError as e:
        print(f"Auth error: {e}")

Trading Response Types

# Example trading response usage
async def trading_response_example():
    suite = await TradingSuite.create(["MNQ"])
    mnq_context = suite["MNQ"]

    # Place order and handle response
    response = await mnq_context.orders.place_limit_order(
        contract_id=mnq_context.instrument_info.id,
        side=0,
        size=1,
        limit_price=21000.0
    )

    if response.success:
        print(f"Order placed successfully: {response.order_id}")
    else:
        print(f"Order failed: {response.error_message}")
        print(f"Error code: {response.error_code}")

    await suite.disconnect()

Statistics & Analytics Models

Statistics Types

# Example statistics usage
async def statistics_example():
    suite = await TradingSuite.create(["MNQ"])
    mnq_context = suite["MNQ"]

    # Get comprehensive statistics
    stats = await suite.get_statistics()

    # Access typed statistics
    print(f"Health Score: {stats.health_score}")
    print(f"API Success Rate: {stats.api_success_rate:.1%}")
    print(f"Total API Calls: {stats.total_api_calls}")
    print(f"Memory Usage: {stats.memory_usage_mb:.1f} MB")

    # Component-specific statistics
    order_stats = await mnq_context.orders.get_stats()
    print(f"Total Orders: {order_stats.total_orders}")
    print(f"Fill Rate: {order_stats.fill_rate:.1%}")
    print(f"Average Fill Time: {order_stats.avg_fill_time_ms:.0f}ms")

    await suite.disconnect()

Enum Types

Order Types

from project_x_py.types import OrderSide, OrderType, OrderStatus

# Example enum usage
async def enum_example():
    suite = await TradingSuite.create(["MNQ"])
    mnq_context = suite["MNQ"]

    # Using enums for type safety
    response = await mnq_context.orders.place_order(
        contract_id=mnq_context.instrument_info.id,
        side=OrderSide.BUY,           # Type-safe enum
        order_type=OrderType.LIMIT,   # Type-safe enum
        size=1,
        price=21000.0
    )

    # Check order status with enum
    if response.status == OrderStatus.FILLED:
        print("Order filled successfully")
    elif response.status == OrderStatus.PENDING:
        print("Order is pending")
    elif response.status == OrderStatus.REJECTED:
        print("Order was rejected")

    await suite.disconnect()

Position Types

from project_x_py.types import PositionType, PositionStatus

# Example position enum usage
async def position_enum_example():
    suite = await TradingSuite.create(["MNQ"])
    mnq_positions = suite["MNQ"].positions

    position = await mnq_positions.get_position("MNQ")
    if position:
        # Check position type
        if position.position_type == PositionType.LONG:
            print("Long position")
        elif position.position_type == PositionType.SHORT:
            print("Short position")

        # Check position status
        if position.status == PositionStatus.OPEN:
            print("Position is open")
        elif position.status == PositionStatus.CLOSED:
            print("Position is closed")

    await suite.disconnect()

Custom Model Creation

Creating Custom Models

from dataclasses import dataclass
from datetime import datetime
from decimal import Decimal
from typing import Optional

@dataclass
class CustomTradingSignal:
    """Custom trading signal model."""
    instrument: str
    signal_type: str  # "buy", "sell", "hold"
    strength: float   # 0.0 to 1.0
    price: Decimal
    timestamp: datetime
    indicators: dict[str, float]
    notes: Optional[str] = None

    def is_strong_signal(self, threshold: float = 0.7) -> bool:
        """Check if signal strength exceeds threshold."""
        return self.strength >= threshold

    def to_dict(self) -> dict:
        """Convert to dictionary for serialization."""
        return {
            "instrument": self.instrument,
            "signal_type": self.signal_type,
            "strength": self.strength,
            "price": float(self.price),
            "timestamp": self.timestamp.isoformat(),
            "indicators": self.indicators,
            "notes": self.notes
        }

# Example custom model usage
async def custom_model_example():
    from project_x_py.indicators import RSI, MACD

    suite = await TradingSuite.create(["MNQ"], timeframes=["5min"])
    mnq_data = suite["MNQ"].data

    # Get data and calculate indicators
    data = await mnq_data.get_data("5min")
    data_with_indicators = data.pipe(RSI, period=14).pipe(MACD)

    if len(data_with_indicators) > 0:
        latest = data_with_indicators.tail(1)

        # Create custom trading signal
        signal = CustomTradingSignal(
            instrument="MNQ",
            signal_type="buy" if latest["rsi_14"].item() < 30 else "hold",
            strength=0.8 if latest["rsi_14"].item() < 30 else 0.3,
            price=Decimal(str(latest["close"].item())),
            timestamp=datetime.now(),
            indicators={
                "rsi": latest["rsi_14"].item(),
                "macd": latest["macd"].item(),
                "signal": latest["macd_signal"].item()
            },
            notes="RSI oversold signal"
        )

        print(f"Signal: {signal.signal_type}")
        print(f"Strength: {signal.strength}")
        print(f"Is strong: {signal.is_strong_signal()}")

    await suite.disconnect()

Model Validation

Data Validation

from pydantic import BaseModel, validator
from decimal import Decimal
from datetime import datetime

class ValidatedOrder(BaseModel):
    """Order model with validation."""
    instrument: str
    side: int
    size: int
    price: Decimal
    timestamp: datetime

    @validator('side')
    def validate_side(cls, v):
        if v not in [0, 1]:  # 0=Buy, 1=Sell
            raise ValueError('Side must be 0 (Buy) or 1 (Sell)')
        return v

    @validator('size')
    def validate_size(cls, v):
        if v <= 0:
            raise ValueError('Size must be positive')
        return v

    @validator('price')
    def validate_price(cls, v):
        if v <= 0:
            raise ValueError('Price must be positive')
        return v

# Example validation usage
try:
    order = ValidatedOrder(
        instrument="MNQ",
        side=0,
        size=1,
        price=Decimal("21000.0"),
        timestamp=datetime.now()
    )
    print("Order validation passed")
except ValueError as e:
    print(f"Validation error: {e}")

Best Practices

Model Usage

# Good: Use type hints for better IDE support
from project_x_py.models import Order, Position
from typing import Optional

async def process_order(order: Order) -> Optional[Position]:
    # Type hints provide better IDE support
    if order.status == "filled":
        return await get_position_for_order(order.order_id)
    return None

# Good: Use enums for type safety
from project_x_py.types import OrderSide, OrderType

side = OrderSide.BUY  # Type-safe
order_type = OrderType.LIMIT  # Type-safe

# L Less safe: Using raw strings/integers
# side = 0  # What does 0 mean?
# order_type = "limit"  # Prone to typos

Error Handling

# Good: Handle model validation errors
try:
    suite = await TradingSuite.create(["MNQ"])
    mnq_context = suite["MNQ"]
    response = await mnq_context.orders.place_limit_order(
        contract_id=mnq_context.instrument_info.id,
        side=0,
        size=1,
        limit_price=21000.0
    )

    if not response.success:
        print(f"Order failed: {response.error_message}")

except ValidationError as e:
    print(f"Invalid order parameters: {e}")
except ProjectXOrderError as e:
    print(f"Order execution error: {e}")

See Also