156 lines
4.7 KiB
Python
156 lines
4.7 KiB
Python
"""Portfolio Risk Management Example.
|
|
|
|
Demonstrates portfolio-level risk controls including
|
|
position limits, drawdown control, and VaR calculations.
|
|
"""
|
|
|
|
from openclaw.portfolio.risk import (
|
|
PortfolioRiskManager,
|
|
PositionConcentrationLimit,
|
|
DrawdownController,
|
|
PortfolioVaR,
|
|
)
|
|
from datetime import datetime
|
|
|
|
|
|
def main():
|
|
"""Run the portfolio risk example."""
|
|
print("=" * 60)
|
|
print("OpenClaw Trading - Portfolio Risk Management")
|
|
print("=" * 60)
|
|
|
|
# 1. Initialize risk manager
|
|
print("\n1. Initializing risk manager...")
|
|
manager = PortfolioRiskManager(
|
|
portfolio_id="demo_portfolio",
|
|
max_concentration_pct=0.20, # Max 20% per position
|
|
max_drawdown_pct=0.10, # Max 10% drawdown
|
|
)
|
|
print(f" Max Position: {manager.concentration_limit.max_concentration_pct:.0%}")
|
|
print(f" Max Drawdown: {manager.drawdown_controller.max_drawdown_threshold:.0%}")
|
|
|
|
# 2. Position concentration check
|
|
print("\n2. Position Concentration Checks:")
|
|
print("-" * 60)
|
|
|
|
test_positions = [
|
|
("AAPL", 1500.0, 10000.0), # 15% - OK
|
|
("TSLA", 2500.0, 10000.0), # 25% - Too high
|
|
("GOOGL", 800.0, 10000.0), # 8% - OK
|
|
]
|
|
|
|
for symbol, position_value, portfolio_value in test_positions:
|
|
concentration = position_value / portfolio_value
|
|
result = manager.concentration_limit.check_concentration(
|
|
symbol=symbol,
|
|
position_value=position_value,
|
|
portfolio_value=portfolio_value
|
|
)
|
|
|
|
status = "✓ ALLOWED" if not result.is_breached else "✗ BLOCKED"
|
|
print(f"\n {symbol}: ${position_value:,.2f} ({concentration:.1%})")
|
|
print(f" Status: {status}")
|
|
if result.is_breached:
|
|
print(f" Reason: Position exceeds 20% concentration limit")
|
|
|
|
# 3. Drawdown control
|
|
print("\n3. Drawdown Control:")
|
|
print("-" * 60)
|
|
|
|
controller = DrawdownController(max_drawdown_threshold=0.10)
|
|
|
|
# Simulate portfolio values
|
|
values = [
|
|
(10000.0, "Start"),
|
|
(10500.0, "Peak"),
|
|
(10200.0, "Small drop"),
|
|
(9500.0, "5% drawdown"),
|
|
(8800.0, "12% drawdown - ALERT!"),
|
|
]
|
|
|
|
peak = 10000.0
|
|
for value, label in values:
|
|
controller.update(value)
|
|
|
|
drawdown = (peak - value) / peak if value < peak else 0
|
|
if value > peak:
|
|
peak = value
|
|
|
|
allowed = controller.is_trading_allowed()
|
|
block_status = "OK" if allowed else "BLOCKED"
|
|
|
|
print(f" {label}: ${value:,.2f} ({drawdown:.1%} drawdown) - {block_status}")
|
|
|
|
# 4. VaR Calculation
|
|
print("\n4. Value at Risk (VaR) Calculation:")
|
|
print("-" * 60)
|
|
|
|
var_calc = PortfolioVaR(
|
|
confidence_level=0.95,
|
|
var_limit_pct=0.05,
|
|
)
|
|
|
|
# Example portfolio
|
|
portfolio_value = 10000.0
|
|
positions = {"AAPL": 3000.0, "GOOGL": 2000.0, "TSLA": 1500.0}
|
|
volatilities = {"AAPL": 0.25, "GOOGL": 0.20, "TSLA": 0.45}
|
|
|
|
var_result = var_calc.calculate_var(
|
|
portfolio_value=portfolio_value,
|
|
positions=positions,
|
|
volatilities=volatilities
|
|
)
|
|
|
|
status = "OK" if not var_result.is_breached else "EXCEEDED"
|
|
|
|
print(f" Portfolio Value: ${portfolio_value:,.2f}")
|
|
print(f" VaR (95%): ${var_result.var_95:,.2f} ({var_result.var_pct:.2%})")
|
|
print(f" VaR (99%): ${var_result.var_99:,.2f}")
|
|
print(f" CVaR (95%): ${var_result.cvar_95:,.2f}")
|
|
print(f" Status: {status}")
|
|
|
|
# 5. Risk alerts
|
|
print("\n5. Risk Alert System:")
|
|
print("-" * 60)
|
|
|
|
from openclaw.portfolio.risk import RiskAlert, RiskAlertLevel
|
|
|
|
alerts = [
|
|
RiskAlert(
|
|
timestamp=datetime.now(),
|
|
alert_type="position_concentration",
|
|
level=RiskAlertLevel.WARNING,
|
|
message="AAPL position exceeds 20% limit",
|
|
symbol="AAPL",
|
|
current_value=0.25,
|
|
threshold=0.20,
|
|
action_taken="blocked",
|
|
),
|
|
RiskAlert(
|
|
timestamp=datetime.now(),
|
|
alert_type="drawdown",
|
|
level=RiskAlertLevel.CRITICAL,
|
|
message="Portfolio drawdown exceeds 10%",
|
|
symbol=None,
|
|
current_value=0.12,
|
|
threshold=0.10,
|
|
action_taken="trading_blocked",
|
|
),
|
|
]
|
|
|
|
for alert in alerts:
|
|
emoji = "⚠️" if alert.level == RiskAlertLevel.WARNING else "🚨"
|
|
print(f"\n {emoji} {alert.level.value.upper()}")
|
|
print(f" Type: {alert.alert_type}")
|
|
print(f" Message: {alert.message}")
|
|
print(f" Current: {alert.current_value:.1%}, Threshold: {alert.threshold:.1%}")
|
|
print(f" Action: {alert.action_taken}")
|
|
|
|
print("\n" + "=" * 60)
|
|
print("Portfolio risk example complete!")
|
|
print("=" * 60)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|