Risk Manager API¶
Comprehensive async risk management system with position sizing, risk validation, stop-loss management, and portfolio risk monitoring.
Overview¶
The RiskManager provides automated risk management capabilities for trading operations. It handles position sizing calculations, trade validation against risk limits, automatic stop-loss and take-profit order placement, and comprehensive risk analytics.
Key Features: - Risk-based position sizing with Kelly criterion support - Daily loss limits and trade count restrictions - Automatic stop-loss and take-profit order placement - Real-time portfolio risk monitoring - Trailing stop functionality - Comprehensive risk metrics and analytics - Statistics integration for performance tracking
Quick Start¶
from project_x_py import TradingSuite, Features
async def basic_risk_management():
# Enable risk manager feature
suite = await TradingSuite.create(
["MNQ"],
features=[Features.RISK_MANAGER]
)
# Access the integrated risk manager
risk = suite["MNQ"].risk_manager
# Calculate position size based on risk
sizing = await risk.calculate_position_size(
entry_price=21000.0,
stop_loss=20975.0,
risk_percent=0.02 # Risk 2% of account
)
print(f"Position size: {sizing.position_size}")
print(f"Risk amount: ${sizing.risk_amount:.2f}")
await suite.disconnect()
Classes¶
RiskManager¶
Core risk management class that handles all risk-related operations.
Constructor¶
RiskManager(
project_x: ProjectXClientProtocol,
order_manager: OrderManagerProtocol,
event_bus: EventBus,
position_manager: PositionManagerProtocol | None = None,
config: RiskConfig | None = None,
data_manager: RealtimeDataManagerProtocol | None = None
)
Parameters:
- project_x
: ProjectX client instance for account and instrument operations
- order_manager
: Order manager for placing and managing protective orders
- event_bus
: Event bus for risk-related events and notifications
- position_manager
: Position manager (can be set later via set_position_manager()
)
- config
: Risk configuration (uses defaults if not provided)
- data_manager
: Optional data manager for market price fetching and ATR calculations
Methods¶
Position Sizing¶
calculate_position_size()
¶
async def calculate_position_size(
self,
entry_price: float,
stop_loss: float,
risk_amount: float | None = None,
risk_percent: float | None = None,
instrument: Instrument | None = None,
use_kelly: bool | None = None,
) -> PositionSizingResponse
Calculate optimal position size based on risk parameters.
Parameters:
- entry_price
: Planned entry price
- stop_loss
: Stop loss price
- risk_amount
: Fixed dollar amount to risk (overrides percentage)
- risk_percent
: Percentage of account to risk (defaults to config value)
- instrument
: Instrument for tick size calculation
- use_kelly
: Override config to use/not use Kelly criterion
Returns: PositionSizingResponse
with calculated size and risk metrics
Example:
# Risk-based position sizing
sizing = await risk_manager.calculate_position_size(
entry_price=21000.0,
stop_loss=20975.0,
risk_percent=0.01, # Risk 1% of account
)
print(f"Position size: {sizing.position_size} contracts")
print(f"Risk amount: ${sizing.risk_amount:.2f}")
print(f"Risk percent: {sizing.risk_percent:.2%}")
Trade Validation¶
validate_trade()
¶
async def validate_trade(
self,
order: Order,
current_positions: list[Position] | None = None,
) -> RiskValidationResponse
Validate a trade against all configured risk rules.
Parameters:
- order
: Order to validate against risk limits
- current_positions
: Current positions (fetched if not provided)
Returns: RiskValidationResponse
with validation result and detailed reasons
Validation Checks: - Daily trade count limit - Maximum concurrent positions - Position size limits - Daily loss limits (dollar amount and percentage) - Portfolio risk concentration - Trading hours restrictions - Correlated position limits
Example:
# Create mock order for validation
mock_order = Order(
id=0,
contractId="CON.F.US.MNQ.U25",
side=OrderSide.BUY,
size=2,
# ... other required fields
)
# Validate against risk rules
validation = await risk_manager.validate_trade(mock_order)
if validation.is_valid:
print("Trade approved")
print(f"Current daily trades: {validation.daily_trades}")
else:
print(f"Trade rejected: {validation.reasons}")
print(f"Warnings: {validation.warnings}")
Risk Order Management¶
attach_risk_orders()
¶
async def attach_risk_orders(
self,
position: Position,
stop_loss: float | None = None,
take_profit: float | None = None,
use_trailing: bool | None = None,
) -> dict[str, Any]
Automatically attach stop-loss and take-profit orders to a position.
Parameters:
- position
: Position to protect with risk orders
- stop_loss
: Override stop loss price (calculated if not provided)
- take_profit
: Override take profit price (calculated if not provided)
- use_trailing
: Override trailing stop configuration
Returns: Dictionary with order details and risk metrics
Stop Loss Calculation:
- "fixed"
: Uses default_stop_distance
in points
- "atr"
: Uses ATR * default_stop_atr_multiplier
- "percentage"
: Uses percentage of entry price
Example:
# Attach risk orders to existing position
risk_orders = await risk_manager.attach_risk_orders(
position=current_position,
stop_loss=20975.0, # Custom stop price
take_profit=21050.0 # Custom target price
)
if risk_orders["bracket_order"].success:
print(f"Stop order: {risk_orders['bracket_order'].stop_order_id}")
print(f"Target order: {risk_orders['bracket_order'].target_order_id}")
print(f"Risk/reward ratio: {risk_orders['risk_reward_ratio']}")
adjust_stops()
¶
async def adjust_stops(
self,
position: Position,
new_stop: float,
order_id: str | None = None,
) -> bool
Adjust stop-loss order for a position.
Parameters:
- position
: Position whose stop loss to adjust
- new_stop
: New stop loss price
- order_id
: Specific order ID to modify (found automatically if not provided)
Returns: True
if adjustment successful
Example:
# Adjust stop loss to breakeven
success = await risk_manager.adjust_stops(
position=current_position,
new_stop=current_position.averagePrice
)
if success:
print("Stop adjusted to breakeven")
Risk Analysis¶
get_risk_metrics()
¶
Get comprehensive risk metrics and portfolio analysis.
Returns: RiskAnalysisResponse
with detailed risk metrics
Metrics Included: - Current risk exposure and limits - Daily loss tracking - Position count and limits - Win rate and profit factor - Sharpe ratio and max drawdown - Individual position risks
Example:
# Get comprehensive risk analysis
metrics = await risk_manager.get_risk_metrics()
print(f"Current risk: ${metrics.current_risk:.2f}")
print(f"Daily loss: ${metrics.daily_loss:.2f}")
print(f"Position count: {metrics.position_count}/{metrics.position_limit}")
print(f"Win rate: {metrics.win_rate:.1%}")
print(f"Sharpe ratio: {metrics.sharpe_ratio:.2f}")
print(f"Max drawdown: {metrics.max_drawdown:.2f}")
# Position-specific risks
for pos_risk in metrics.position_risks:
print(f"{pos_risk['symbol']}: ${pos_risk['risk_amount']:.2f} "
f"({pos_risk['risk_percent']:.2%})")
Configuration Management¶
set_position_manager()
¶
Set the position manager to resolve circular dependencies.
Parameters:
- position_manager
: Position manager instance for risk calculations
Note: This method must be called before any position-related operations if not provided in constructor.
Stop Loss Calculations¶
calculate_stop_loss()
¶
async def calculate_stop_loss(
self,
entry_price: float,
side: OrderSide,
atr_value: float | None = None
) -> float
Calculate stop loss price based on configuration.
Parameters:
- entry_price
: Entry price for the trade
- side
: Order side (BUY or SELL)
- atr_value
: ATR value for dynamic stop calculation
Returns: Calculated stop loss price
Example:
# Calculate stop loss for long position
stop_price = await risk_manager.calculate_stop_loss(
entry_price=21000.0,
side=OrderSide.BUY,
atr_value=15.0 # Current ATR
)
print(f"Stop loss: {stop_price}")
calculate_take_profit()
¶
async def calculate_take_profit(
self,
entry_price: float,
stop_loss: float,
side: OrderSide,
risk_reward_ratio: float | None = None,
) -> float
Calculate take profit price based on risk/reward ratio.
Parameters:
- entry_price
: Entry price for the trade
- stop_loss
: Stop loss price
- side
: Order side (BUY or SELL)
- risk_reward_ratio
: Risk/reward ratio (uses config default if None)
Returns: Calculated take profit price
Trade Recording¶
record_trade_result()
¶
Record trade result for risk analysis and Kelly criterion calculations.
Parameters:
- position_id
: Position identifier
- pnl
: Profit/loss amount
- duration_seconds
: Trade duration in seconds
Example:
# Record completed trade
await risk_manager.record_trade_result(
position_id="pos_123",
pnl=150.0, # $150 profit
duration_seconds=3600 # 1 hour trade
)
add_trade_result()
¶
async def add_trade_result(
self,
instrument: str,
pnl: float,
entry_price: float | None = None,
exit_price: float | None = None,
size: int | None = None,
side: OrderSide | None = None,
) -> None
Add trade result with detailed information to history.
Parameters:
- instrument
: Instrument identifier
- pnl
: Profit/loss amount
- entry_price
: Entry price
- exit_price
: Exit price
- size
: Position size
- side
: Order side
Portfolio Analysis¶
analyze_portfolio_risk()
¶
Analyze overall portfolio risk and concentration.
Returns: Dictionary with portfolio risk analysis
Example:
analysis = await risk_manager.analyze_portfolio_risk()
print(f"Total risk: ${analysis['total_risk']:.2f}")
print(f"Position count: {len(analysis['position_risks'])}")
analyze_trade_risk()
¶
async def analyze_trade_risk(
self,
instrument: str,
entry_price: float,
stop_loss: float,
take_profit: float,
position_size: int,
) -> dict[str, Any]
Analyze risk/reward for a specific trade setup.
Parameters:
- instrument
: Instrument identifier
- entry_price
: Planned entry price
- stop_loss
: Stop loss price
- take_profit
: Take profit price
- position_size
: Position size in contracts
Returns: Dictionary with risk/reward analysis
Cleanup¶
cleanup()
¶
Clean up all resources and cancel active tasks (trailing stops, monitoring).
Example:
RiskConfig¶
Configuration dataclass for risk management parameters.
@dataclass
class RiskConfig:
# Per-trade risk limits
max_risk_per_trade: Decimal = Decimal("0.01") # 1% per trade
max_risk_per_trade_amount: Decimal | None = None # Dollar limit
# Daily risk limits
max_daily_loss: Decimal = Decimal("0.03") # 3% daily loss
max_daily_loss_amount: Decimal | None = None # Dollar limit
max_daily_trades: int = 10 # Maximum trades per day
# Position limits
max_position_size: int = 10 # Maximum contracts per position
max_positions: int = 3 # Maximum concurrent positions
max_portfolio_risk: Decimal = Decimal("0.05") # 5% total portfolio risk
# Stop-loss configuration
use_stop_loss: bool = True
stop_loss_type: str = "fixed" # "fixed", "atr", "percentage"
default_stop_distance: Decimal = Decimal("50") # Points
default_stop_atr_multiplier: Decimal = Decimal("2.0") # ATR multiplier
# Take-profit configuration
use_take_profit: bool = True
default_risk_reward_ratio: Decimal = Decimal("2.0") # 1:2 risk/reward
# Trailing stop configuration
use_trailing_stops: bool = True
trailing_stop_distance: Decimal = Decimal("20") # Points behind price
trailing_stop_trigger: Decimal = Decimal("30") # Profit points before trailing
# Advanced features
scale_in_enabled: bool = False # Allow position scaling
scale_out_enabled: bool = True # Allow partial exits
martingale_enabled: bool = False # DANGEROUS: Double down on losses
# Time-based rules
restrict_trading_hours: bool = False
allowed_trading_hours: list[tuple[str, str]] = [("09:30", "16:00")]
avoid_news_events: bool = True
news_blackout_minutes: int = 30 # Minutes before/after news
# Correlation limits
max_correlated_positions: int = 2 # Max correlated instruments
correlation_threshold: Decimal = Decimal("0.7") # Correlation coefficient
# Kelly Criterion parameters
use_kelly_criterion: bool = False
kelly_fraction: Decimal = Decimal("0.25") # Use 25% of Kelly recommendation
min_trades_for_kelly: int = 30 # Minimum trades before using Kelly
Example Configuration:
from project_x_py.risk_manager import RiskConfig
from decimal import Decimal
# Conservative risk configuration
conservative_config = RiskConfig(
max_risk_per_trade=Decimal("0.005"), # 0.5% per trade
max_daily_loss=Decimal("0.02"), # 2% daily loss limit
max_daily_trades=5, # Max 5 trades per day
max_position_size=2, # Max 2 contracts
max_positions=2, # Max 2 concurrent positions
default_stop_distance=Decimal("25"), # Tight 25-point stops
default_risk_reward_ratio=Decimal("3.0"), # 1:3 risk/reward
use_trailing_stops=True,
trailing_stop_distance=Decimal("15"),
use_kelly_criterion=False # Disable Kelly for conservative approach
)
# Aggressive risk configuration
aggressive_config = RiskConfig(
max_risk_per_trade=Decimal("0.02"), # 2% per trade
max_daily_loss=Decimal("0.05"), # 5% daily loss limit
max_daily_trades=20, # Max 20 trades per day
max_position_size=10, # Max 10 contracts
max_positions=5, # Max 5 concurrent positions
stop_loss_type="atr", # ATR-based stops
default_stop_atr_multiplier=Decimal("1.5"), # 1.5x ATR
use_kelly_criterion=True, # Enable Kelly sizing
kelly_fraction=Decimal("0.5"), # Use 50% of Kelly
scale_in_enabled=True, # Allow scaling into positions
)
ManagedTrade¶
Context manager for risk-managed trade execution with automatic position sizing and risk control.
class ManagedTrade:
def __init__(
self,
risk_manager: RiskManager,
order_manager: OrderManagerProtocol,
position_manager: PositionManagerProtocol,
instrument_id: str,
data_manager: Any | None = None,
event_bus: Any | None = None,
max_risk_percent: float | None = None,
max_risk_amount: float | None = None,
)
Parameters:
- risk_manager
: Risk manager instance for calculations and validation
- order_manager
: Order manager for placing trades
- position_manager
: Position manager for position tracking
- instrument_id
: Instrument/contract ID to trade
- data_manager
: Optional data manager for market price fetching
- event_bus
: Optional event bus for event-driven order tracking
- max_risk_percent
: Override max risk percentage for this trade
- max_risk_amount
: Override max risk dollar amount for this trade
Methods¶
Position Entry¶
enter_long()
¶
async def enter_long(
self,
entry_price: float | None = None,
stop_loss: float | None = None,
take_profit: float | None = None,
size: int | None = None,
order_type: OrderType = OrderType.MARKET,
) -> dict[str, Any]
Enter a long position with automatic risk management.
Parameters:
- entry_price
: Limit order price (None for market entry)
- stop_loss
: Stop loss price (auto-calculated if not provided)
- take_profit
: Take profit price (auto-calculated if not provided)
- size
: Position size (auto-calculated if not provided)
- order_type
: Order type (MARKET or LIMIT)
Returns: Dictionary with entry details, orders, and risk metrics
Example:
async with ManagedTrade(
risk_manager=suite["MNQ"].risk_manager,
order_manager=suite["MNQ"].orders,
position_manager=suite["MNQ"].positions,
instrument_id=suite["MNQ"].instrument_info.id,
max_risk_percent=0.02
) as trade:
# Enter long position with automatic sizing and risk management
result = await trade.enter_long(
entry_price=21000.0, # Limit entry
stop_loss=20975.0, # 25-point stop
take_profit=21050.0, # 50-point target
order_type=OrderType.LIMIT
)
if result["entry_order"]:
print(f"Long position entered:")
print(f" Size: {result['size']} contracts")
print(f" Risk: ${result['risk_amount']:.2f}")
print(f" Entry: {result['entry_order'].id}")
print(f" Stop: {result['stop_order'].id if result['stop_order'] else 'None'}")
print(f" Target: {result['target_order'].id if result['target_order'] else 'None'}")
enter_short()
¶
async def enter_short(
self,
entry_price: float | None = None,
stop_loss: float | None = None,
take_profit: float | None = None,
size: int | None = None,
order_type: OrderType = OrderType.MARKET,
) -> dict[str, Any]
Enter a short position with automatic risk management.
Parameters: Same as enter_long()
Returns: Dictionary with entry details, orders, and risk metrics
Position Management¶
scale_in()
¶
async def scale_in(
self,
additional_size: int,
new_stop_loss: float | None = None,
) -> dict[str, Any]
Scale into existing position with risk validation.
Parameters:
- additional_size
: Additional contracts to add
- new_stop_loss
: New stop loss for entire position
Requires: scale_in_enabled=True
in RiskConfig
scale_out()
¶
Scale out of position with partial exit.
Parameters:
- exit_size
: Number of contracts to exit
- limit_price
: Limit price for exit (market if None)
close_position()
¶
Close entire position at market price.
Returns: Dictionary with close details or None if no position
adjust_stop()
¶
Adjust stop loss for current position.
Parameters:
- new_stop_loss
: New stop loss price
Returns: True if adjustment successful
Trade Monitoring¶
wait_for_fill()
¶
Wait for entry order to be filled.
Parameters:
- timeout
: Maximum wait time in seconds
Returns: True if order filled within timeout
monitor_position()
¶
Get current position status and P&L.
Returns: Dictionary with position details and unrealized P&L
get_trade_summary()
¶
Get comprehensive trade summary with all details.
Returns: Dictionary with complete trade information
Usage Examples¶
Basic Risk-Managed Trading¶
from project_x_py import TradingSuite, Features
from project_x_py.risk_manager import RiskConfig, ManagedTrade
from decimal import Decimal
async def basic_risk_trading():
# Setup with risk management
config = RiskConfig(
max_risk_per_trade=Decimal("0.01"), # 1% risk per trade
max_daily_loss=Decimal("0.03"), # 3% daily loss limit
use_stop_loss=True,
default_stop_distance=Decimal("25"), # 25-point stops
default_risk_reward_ratio=Decimal("2.0") # 1:2 risk/reward
)
suite = await TradingSuite.create(
["MNQ"],
features=[Features.RISK_MANAGER],
risk_config=config
)
mnq_context = suite["MNQ"]
# Execute risk-managed trade
async with ManagedTrade(
risk_manager=mnq_context.risk_manager,
order_manager=mnq_context.orders,
position_manager=mnq_context.positions,
instrument_id=mnq_context.instrument_info.id,
data_manager=mnq_context.data,
max_risk_percent=0.015 # Override to 1.5% for this trade
) as trade:
# Enter long with automatic position sizing
result = await trade.enter_long(
entry_price=21000.0,
order_type=OrderType.LIMIT
)
if result["entry_order"]:
print(f"Trade executed:")
print(f" Position size: {result['size']} contracts")
print(f" Risk amount: ${result['risk_amount']:.2f}")
# Wait for fill
filled = await trade.wait_for_fill(timeout=60)
if filled:
print("Position filled successfully")
# Monitor position
while True:
status = await trade.monitor_position()
print(f"P&L: ${status['pnl']:.2f}")
# Exit condition example
if status['pnl'] >= 100: # Take profits at $100
await trade.scale_out(1) # Partial exit
break
elif status['pnl'] <= -50: # Emergency exit
await trade.close_position()
break
await asyncio.sleep(5) # Check every 5 seconds
# Get final risk metrics
metrics = await mnq_context.risk_manager.get_risk_metrics()
print(f"Daily P&L: ${metrics.daily_loss:.2f}")
print(f"Trades today: {metrics.daily_trades}")
await suite.disconnect()
Advanced Portfolio Risk Management¶
async def advanced_portfolio_risk():
# Setup multiple instruments with correlation limits
config = RiskConfig(
max_risk_per_trade=Decimal("0.02"),
max_positions=5,
max_correlated_positions=2, # Max 2 correlated positions
correlation_threshold=Decimal("0.7"),
use_kelly_criterion=True,
kelly_fraction=Decimal("0.25")
)
suite = await TradingSuite.create(
["MNQ", "ES", "RTY"],
features=[Features.RISK_MANAGER],
risk_config=config
)
# Portfolio risk monitoring
async def monitor_portfolio_risk():
while True:
# This would ideally be a single call to a portfolio-level risk manager
# For this example, we'll check the risk manager of the primary instrument
analysis = await suite["MNQ"].risk_manager.analyze_portfolio_risk()
print(f"Portfolio Risk Analysis:")
print(f" Total risk: ${analysis['total_risk']:.2f}")
print(f" Position count: {len(analysis['position_risks'])}")
# Check for excessive risk
for pos_risk in analysis['position_risks']:
if pos_risk['risk']['percent'] > 0.03: # 3% position risk
print(f"High risk position detected: {pos_risk['instrument']}")
await asyncio.sleep(60) # Check every minute
# Start monitoring task
monitor_task = asyncio.create_task(monitor_portfolio_risk())
try:
# Execute trades with portfolio-wide risk awareness
# In a real scenario, you would have logic to decide which instrument to trade
print("Trading logic would be executed here.")
await asyncio.sleep(1)
finally:
monitor_task.cancel()
await suite.disconnect()
Response Objects¶
PositionSizingResponse¶
@dataclass
class PositionSizingResponse:
position_size: int # Calculated position size in contracts
risk_amount: float # Actual risk amount in dollars
risk_percent: float # Risk as percentage of account
entry_price: float # Entry price used in calculation
stop_loss: float # Stop loss price used
tick_size: float # Instrument tick size
account_balance: float # Current account balance
kelly_fraction: float | None # Kelly fraction if applicable
max_position_size: int # Maximum allowed position size
sizing_method: str # Method used ("fixed_risk", "kelly", etc.)
RiskValidationResponse¶
@dataclass
class RiskValidationResponse:
is_valid: bool # Whether trade passes all risk checks
reasons: list[str] # Reasons for rejection (if any)
warnings: list[str] # Warnings but not rejections
current_risk: float # Current portfolio risk
daily_loss: float # Current daily loss
daily_trades: int # Number of trades today
position_count: int # Current position count
portfolio_risk: float # Total portfolio risk percentage
RiskAnalysisResponse¶
@dataclass
class RiskAnalysisResponse:
current_risk: float # Current risk exposure
max_risk: float # Maximum allowed risk
daily_loss: float # Current daily loss
daily_loss_limit: float # Daily loss limit
position_count: int # Current positions
position_limit: int # Maximum positions
daily_trades: int # Trades executed today
daily_trade_limit: int # Maximum daily trades
win_rate: float # Historical win rate
profit_factor: float # Gross profit / gross loss
sharpe_ratio: float # Risk-adjusted return metric
max_drawdown: float # Maximum drawdown
position_risks: list[dict] # Individual position risk details
risk_per_trade: float # Risk per trade setting
account_balance: float # Current account balance
margin_used: float # Used margin
margin_available: float # Available margin
Error Handling¶
The risk manager raises specific exceptions for different error conditions:
from project_x_py.exceptions import InvalidOrderParameters
try:
sizing = await risk_manager.calculate_position_size(
entry_price=21000.0,
stop_loss=21000.0, # Same as entry - invalid!
risk_percent=0.01
)
except InvalidOrderParameters as e:
print(f"Invalid parameters: {e}")
# Risk validation errors
validation = await risk_manager.validate_trade(order)
if not validation.is_valid:
# Handle rejection reasons
for reason in validation.reasons:
print(f"Rejection reason: {reason}")
# Check warnings
for warning in validation.warnings:
print(f"Warning: {warning}")
Events¶
The risk manager emits various events through the event bus:
from project_x_py import EventType
# Risk limit exceeded event
async def on_risk_limit(event):
print(f"Risk limit exceeded: {event.data}")
# Take protective action
# Risk orders placed event
async def on_risk_orders(event):
data = event.data
print(f"Risk orders placed for position {data['position'].id}")
print(f"Stop loss: {data['stop_loss']}")
print(f"Take profit: {data['take_profit']}")
# Subscribe to risk events
suite = await TradingSuite.create(["MNQ"], features=[Features.RISK_MANAGER])
mnq_context = suite["MNQ"]
await mnq_context.event_bus.on("risk_limit_exceeded", on_risk_limit)
await mnq_context.event_bus.on("risk_orders_placed", on_risk_orders)
Statistics Integration¶
The RiskManager extends BaseStatisticsTracker
and provides comprehensive metrics:
# Get risk manager statistics
stats = await suite["MNQ"].risk_manager.get_statistics()
print(f"Risk Manager Statistics:")
print(f" Status: {stats['status']}")
print(f" Position size calculations: {stats.get('position_size_calculations', 0)}")
print(f" Trade validations: {stats.get('trade_validations', 0)}")
print(f" Valid trades: {stats.get('valid_trades', 0)}")
print(f" Invalid trades: {stats.get('invalid_trades', 0)}")
print(f" Risk orders attached: {stats.get('risk_orders_attached', 0)}")
print(f" Stop adjustments: {stats.get('stop_adjustments', 0)}")
print(f" Winning trades: {stats.get('winning_trades', 0)}")
print(f" Losing trades: {stats.get('losing_trades', 0)}")
# Risk-specific gauges
print(f"Current Metrics:")
print(f" Win rate: {stats.get('win_rate_percent', 0):.1f}%")
print(f" Largest win: ${stats.get('largest_win', 0):.2f}")
print(f" Largest loss: ${stats.get('largest_loss', 0):.2f}")
print(f" Daily trades: {stats.get('daily_trades_count', 0)}")
print(f" Portfolio risk: {stats.get('current_portfolio_risk', 0):.2%}")
Best Practices¶
1. Always Configure Risk Limits¶
# ✓ Good: Define clear risk parameters
config = RiskConfig(
max_risk_per_trade=Decimal("0.01"), # Never risk more than 1%
max_daily_loss=Decimal("0.03"), # Stop trading at 3% daily loss
max_positions=3, # Limit concurrent positions
use_stop_loss=True # Always use stops
)
# ✗ Bad: Using defaults without understanding them
config = RiskConfig() # What are the defaults? Do they fit my strategy?
2. Validate Before Trading¶
# ✓ Good: Validate every trade against risk rules
validation = await risk_manager.validate_trade(order)
if validation.is_valid:
# Execute trade
pass
else:
logger.warning(f"Trade rejected: {validation.reasons}")
# ✗ Bad: Skip validation and risk blowing account
# Direct order placement without risk checks
3. Use ManagedTrade for Consistency¶
# ✓ Good: Consistent risk management with ManagedTrade
suite = await TradingSuite.create(["MNQ"], features=[Features.RISK_MANAGER])
mnq_context = suite["MNQ"]
async with ManagedTrade(
risk_manager=mnq_context.risk_manager,
order_manager=mnq_context.orders,
position_manager=mnq_context.positions,
instrument_id=mnq_context.instrument_info.id
) as trade:
result = await trade.enter_long(entry_price=21000.0)
# Automatic position sizing, stops, targets
# ✗ Bad: Manual risk calculations (error-prone)
# Manual position size = account * 0.01 / (entry - stop)
# Manual order placement
# Manual stop/target attachment
4. Monitor Risk Continuously¶
# ✓ Good: Active risk monitoring
async def risk_monitor():
while trading_active:
metrics = await risk_manager.get_risk_metrics()
if metrics.daily_loss > max_daily_loss:
await emergency_close_all()
await asyncio.sleep(60)
# ✗ Bad: Set and forget - no monitoring
# Place trades and ignore running risk
5. Record Trade Results¶
# ✓ Good: Track performance for Kelly criterion and analysis
await risk_manager.record_trade_result(
position_id="pos_123",
pnl=trade_pnl,
duration_seconds=trade_duration
)
# ✗ Bad: No performance tracking
# Miss opportunities for strategy improvement
See Also¶
- Position Manager API - Portfolio and position tracking
- Order Manager API - Order placement and management
- Statistics API - Performance metrics and analytics
- Risk Management Guide - Comprehensive risk management guide