Multi-Asset Portfolio Strategies: Beyond Equities

Diversify across equities, derivatives, commodities, and currencies to build robust all-weather portfolios for Indian markets.

Equity-only portfolios suffer in bear markets. Multi-asset portfolios—combining stocks, options, commodities, currencies—provide true diversification, reducing volatility while maintaining returns.

This guide builds multi-asset strategies for Indian markets, leveraging NSE equities, Nifty derivatives, MCX commodities, and currency futures.

Asset Class Characteristics in India

Correlation Matrix (2020-2024)

AssetNifty 50GoldUSD/INRCrude
Nifty 501.00-0.15-0.300.40
Gold-0.151.000.600.20
USD/INR-0.300.601.000.35
Crude0.400.200.351.00

Key Insights:

  • Gold negatively correlated with Nifty (hedge)
  • USD/INR rises when Nifty falls (FII outflows)
  • Crude positively correlated (India net importer)
import pandas as pd
import numpy as np
from scipy.optimize import minimize

class MultiAssetPortfolio:
    """
    Multi-asset portfolio optimization
    """
    
    def __init__(self, asset_returns: pd.DataFrame):
        self.returns = asset_returns
        self.mean_returns = asset_returns.mean()
        self.cov_matrix = asset_returns.cov()
        
    def optimize_weights(self, target_return: float = None) -> np.array:
        """
        Optimize asset weights for maximum Sharpe ratio
        """
        n_assets = len(self.mean_returns)
        
        # Objective: Negative Sharpe (minimize negative = maximize positive)
        def neg_sharpe(weights):
            portfolio_return = np.dot(weights, self.mean_returns) * 252
            portfolio_std = np.sqrt(np.dot(weights.T, np.dot(self.cov_matrix * 252, weights)))
            sharpe = portfolio_return / portfolio_std
            return -sharpe
        
        # Constraints
        constraints = [
            {'type': 'eq', 'fun': lambda w: np.sum(w) - 1}  # Weights sum to 1
        ]
        
        if target_return:
            constraints.append({
                'type': 'eq',
                'fun': lambda w: np.dot(w, self.mean_returns) * 252 - target_return
            })
        
        # Bounds (0 to 1 for long-only)
        bounds = tuple((0, 1) for _ in range(n_assets))
        
        # Initial guess
        init_weights = np.array([1/n_assets] * n_assets)
        
        # Optimize
        result = minimize(
            neg_sharpe,
            init_weights,
            method='SLSQP',
            bounds=bounds,
            constraints=constraints
        )
        
        return result.x

# Example: Optimize across 4 assets
asset_returns = pd.DataFrame({
    'Nifty50': np.random.randn(1000) * 0.015 + 0.0005,
    'Gold': np.random.randn(1000) * 0.010 + 0.0003,
    'USDINR': np.random.randn(1000) * 0.008 + 0.0002,
    'Crude': np.random.randn(1000) * 0.025 + 0.0004
})

portfolio = MultiAssetPortfolio(asset_returns)
optimal_weights = portfolio.optimize_weights()

print("Optimal Weights:")
for asset, weight in zip(asset_returns.columns, optimal_weights):
    print(f"  {asset}: {weight:.1%}")