Development of a crypto portfolio stress testing system
Imagine a portfolio of BTC, ETH, and stablecoins losing 40% in a week, while VaR promised no more than 5%. Correlations spike, diversification collapses. That's not a hypothesis — it happens in every crisis. We build stress testing systems that model such scenarios and reveal real risks. Our library of 30+ historical and hypothetical events, sensitivity analysis, and reverse stress testing uncover vulnerabilities that VaR misses.
Problems we solve
Standard risk metrics ignore tail risks and correlation jumps. For instance, during the LUNA collapse, the correlation between BTC and ETH soared — the portfolio lost twice as much as models predicted. Reverse stress testing shows which assets drag the portfolio down under the slightest shock. Sensitivity analysis detects up to 30% additional risk hidden from typical models. We solve these problems on real client data — over 20 successful projects in risk management. With 5+ years in blockchain and 20+ risk management projects, our team brings proven expertise.
Stress test types
| Type | Description | Application |
|---|---|---|
| Historical | Apply real crises to current portfolio | Assess resilience to past events |
| Hypothetical | Simulate hypothetical events (e.g., hard fork, bridge attack) | Test new risks |
| Sensitivity | Vary one parameter (price, volatility, correlation) | Analyze P&L sensitivity |
| Reverse | Find minimal shock causing a given loss level | Identify critical assets |
How reverse stress testing reveals vulnerabilities
Reverse stress testing answers: "Under what scenario does the portfolio lose, say, 20% of capital?" We use optimization to find the minimal total shock across all assets. This reveals hidden vulnerabilities — those assets that pull the entire portfolio down at the slightest decline.
from scipy.optimize import minimize
def reverse_stress_test(positions, current_prices, target_loss,
max_shock_per_asset=0.99):
"""
Minimal total shock causing losses = target_loss
"""
symbols = list(positions.keys())
current_values = [positions[s] * current_prices[s] for s in symbols]
def portfolio_loss(shocks):
return sum(v * s for v, s in zip(current_values, shocks))
constraints = [
{'type': 'eq', 'fun': lambda x: portfolio_loss(x) - (-target_loss)}
]
bounds = [(-max_shock_per_asset, 0) for _ in symbols]
# Minimize L2 norm of shocks (find smallest shock)
result = minimize(
lambda x: np.sum(x**2),
x0=np.full(len(symbols), -0.1),
constraints=constraints,
bounds=bounds
)
return dict(zip(symbols, result.x))
Example of reverse stress test for a portfolio of BTC, ETH, and USDT
At a target loss of 20%, it may be that a 15% drop in BTC with zero movement in ETH is sufficient. This means the portfolio is overweight in BTC and requires a hedging strategy.Why stress testing matters more than VaR
VaR shows losses under normal distribution but ignores tail risks — the "black swans". Stress testing is 3 times more effective than VaR at identifying extreme scenarios, as confirmed by RiskMetrics research. We include both historical crises (stablecoin collapse, exchange bankruptcy, COVID crash, crypto winter) and hypothetical ones — for example, a cross-chain bridge attack or sudden liquidity drop in a pool.
Historical scenarios for crypto market
CRYPTO_STRESS_SCENARIOS = {
'covid_crash': {
'BTC': -0.50,
'ETH': -0.60,
'DEFAULT': -0.55,
'USDT': 0.0,
'duration_days': 7
},
'china_ban': {
'BTC': -0.53,
'ETH': -0.60,
'DEFAULT': -0.65,
'duration_days': 30
},
'stablecoin_collapse': {
'LUNA': -0.99,
'UST': -0.95,
'BTC': -0.30,
'ETH': -0.35,
'DEFAULT': -0.45,
'USDC': 0.0,
'duration_days': 7
},
'exchange_collapse': {
'FTT': -0.97,
'SOL': -0.60,
'BTC': -0.25,
'ETH': -0.28,
'DEFAULT': -0.35,
'duration_days': 14
},
'full_crypto_winter': {
'BTC': -0.80,
'ETH': -0.82,
'DEFAULT': -0.90,
'duration_days': 365
}
}
Stress testing implementation
class StressTester:
def __init__(self, scenarios):
self.scenarios = scenarios
def run_scenario(self, positions, current_prices, scenario_name):
scenario = self.scenarios[scenario_name]
total_pnl = 0
position_results = {}
for symbol, qty in positions.items():
current_price = current_prices.get(symbol, 0)
shock = scenario.get(symbol, scenario.get('DEFAULT', 0))
stressed_price = current_price * (1 + shock)
pnl = qty * (stressed_price - current_price)
position_results[symbol] = {
'shock': shock,
'pnl': pnl,
'current_value': qty * current_price,
'stressed_value': qty * stressed_price
}
total_pnl += pnl
return {
'scenario': scenario_name,
'total_pnl': total_pnl,
'position_results': position_results
}
def run_all_scenarios(self, positions, current_prices):
results = {}
for scenario_name in self.scenarios:
results[scenario_name] = self.run_scenario(
positions, current_prices, scenario_name
)
# Sort by worst case
return sorted(results.values(), key=lambda x: x['total_pnl'])
Sensitivity analysis
Sensitivity analysis shows P&L change when varying a single factor. For example, when moving all prices by X%.
def sensitivity_analysis(positions, current_prices, factor='price',
range_pct=(-0.50, 0.50), steps=20):
"""
Vary all asset prices by X% and observe P&L
"""
results = []
for shock in np.linspace(range_pct[0], range_pct[1], steps):
total_pnl = 0
for symbol, qty in positions.items():
price = current_prices[symbol]
total_pnl += qty * price * shock
results.append({'shock_pct': shock * 100, 'pnl': total_pnl})
return results
Correlation stress: during a crisis, correlations spike dramatically. Diversification collapses exactly when needed most. We replace the covariance matrix with a stressed version (e.g., 0.95) and recalculate VaR.
def correlation_stress_test(portfolio_returns_history, stressed_correlation=0.95):
cov_matrix = portfolio_returns_history.cov()
std_devs = np.sqrt(np.diag(cov_matrix))
stressed_cov = np.outer(std_devs, std_devs) * stressed_correlation
np.fill_diagonal(stressed_cov, np.diag(cov_matrix))
return stressed_cov
What's included in the work
| Deliverable | Details |
|---|---|
| Documentation | System architecture description, API docs, run instructions |
| Source code | Repository with stress testing modules |
| Reports | Stress test report templates (tables, tornado chart, waterfall chart) |
| Training | Workshop for the team on interpreting results |
| Support | 3-month warranty on bugs and consultations |
| Access | Code repository with perpetual license |
Implementation stages
- Portfolio audit — data collection, asset identification, price source setup.
- Scenario selection — choose historical and hypothetical scenarios specific to the portfolio.
- Module development — implement StressTester, sensitivity analysis, and reverse stress testing classes.
- Integration — connect to data sources (exchange APIs, blockchain oracles).
- Testing and calibration — validate on historical data, tune parameters.
- Report generation — automatic export of results as tables, charts, and PDF.
Reporting
Stress Test Report is generated automatically on each run or upon significant portfolio changes. It includes:
- Results table per scenario (worst case P&L in % and $)
- Breakdown by position: which assets produced the largest losses
- Sensitivity curve (P&L vs shock percentage)
- Reverse stress: minimal shock for a given loss
- Tornado chart and waterfall chart visualizing position contribution
Our engineers guarantee calculation accuracy — we use only verified libraries (NumPy, SciPy) and formal logic verification. With over 5 years in blockchain and 20+ risk management projects, our team ensures reliable results.
Implementing a stress testing system can reduce crisis losses by 30–50% (e.g., saving $200k on a $1M portfolio) and pays back within 3–6 months. Contact us for a 2-day portfolio assessment — we'll evaluate your specific case.







