Return Asymmetry Effect in Commodity Futures

Log in to collect

Onsite backtest IDE

Quant Buffet native backtest IDE

Edit and run Quant Buffet Python for Return Asymmetry Effect in Commodity Futures in the browser. Results update live with equity, drawdown, and metrics charts. Allowed: backtest.data, backtest.engine, backtest.metrics, numpy, pandas. Define ASSETS and make_on_day(prices). Shortcut: Ctrl+Enter. API docs →

Ready — edit code, then Run backtest.
IDE · 40 lines
Quant Buffet syntax cheat sheet (copy / insert)

Paste these fragments into the editor. The sandbox rejects QuantConnect, os, and network libraries.

Required imports
Only these libraries are allowed in the sandbox.
from __future__ import annotations

import numpy as np
import pandas as pd

from backtest.data import load_daily_prices
from backtest.engine import EngineConfig, PortfolioEngine
from backtest.metrics import compute_metrics
ASSETS list (whitelisted ETFs)
Module-level list. Tickers must be in the Quant Buffet whitelist.
ASSETS = ["SPY", "QQQ", "TLT", "GLD", "BIL"]
make_on_day contract
Must return (on_day, ready). on_day calls engine.set_target_weights.
def make_on_day(prices: pd.DataFrame):
    cols = [c for c in ASSETS if c in prices.columns]
    sma = prices[cols].rolling(200, min_periods=200).mean()
    state = {"last": None}

    def on_day(engine: PortfolioEngine, dt: pd.Timestamp) -> None:
        if sma.loc[dt].isna().all():
            return
        key = (dt.year, dt.month)
        if state["last"] == key:
            return
        state["last"] = key
        long = [
            s for s in cols
            if pd.notna(prices.at[dt, s]) and pd.notna(sma.at[dt, s])
            and prices.at[dt, s] > sma.at[dt, s]
        ]
        weights = {} if not long else {s: 1.0 / len(long) for s in long}
        engine.set_target_weights(dt, weights)

    ready = sma.dropna(how="all").index.min() if sma.notna().any().any() else None
    return on_day, ready
Set target weights
Weights should sum to about 1.0. Empty dict = 100% cash.
engine.set_target_weights(dt, {"SPY": 0.60, "BIL": 0.40})

Live backtest performance

CAGR
6.77%
Sharpe
0.74
Max DD
-19.09%
Vol
9.43%
Sortino
1.11
Beta
0.28
Up days
51%

Run the backtest to populate charts.

Export to your platform

Transform Quant Buffet lab code (ASSETS + make_on_day / PortfolioEngine) into native classes for a third-party IDE — then copy and paste.

Run in: QuantConnect Cloud or LEAN CLI · QCAlgorithm with Equity securities and monthly rebalance.

Detected pattern: SMA trendAssets: SPY, TLT, GLD, BIL
# Generated from Quant Buffet → QuantConnect LEAN
# Strategy: Return Asymmetry Effect in Commodity Futures
# Detected pattern: SMA trend
# Source uses Quant Buffet lab APIs (ASSETS + make_on_day / PortfolioEngine).
# Review fees, data, and risk before live trading — educational export only.

from AlgorithmImports import *


class QuantBuffetExport(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2010, 1, 1)
        self.SetCash(100000)
        tickers = ["SPY", "TLT", "GLD", "BIL"]
        self.symbols = []
        for t in tickers:
            if "-" in t:  # crypto proxy e.g. BTC-USD
                self.symbols.append(self.AddCrypto(t.replace("-USD", ""), Resolution.Daily).Symbol)
            else:
                self.symbols.append(self.AddEquity(t, Resolution.Daily).Symbol)
        self.Schedule.On(
            self.DateRules.MonthStart(self.symbols[0]),
            self.TimeRules.AfterMarketOpen(self.symbols[0], 30),
            self.Rebalance,
        )
        # Logic: Long assets where close > SMA(200); equal-weight; monthly.

    def Rebalance(self):
        longs = []
        for symbol in self.symbols:
            hist = self.History(symbol, 200 + 5, Resolution.Daily)
            if hist.empty: continue
            close = hist["close"].unstack(level=0).iloc[:, 0] if hasattr(hist["close"], "unstack") else hist["close"]
            if len(close) < 200: continue
            if float(close.iloc[-1]) > float(close.iloc[-200:].mean()):
                longs.append(symbol)
        weight = 1.0 / len(longs) if longs else 0.0
        for symbol in self.symbols:
            self.SetHoldings(symbol, weight if symbol in longs else 0.0)

Exported code uses the platform’s native classes and libraries. Install dependencies in your third-party IDE, then run. Validate before live trading.

Academic paper

Return Asymmetry in Commodity Futures

AuthorsLadislav Ďurian; Matus Padysak

Institute
  • ?Quantpedia.com
  • SKComenius University Bratislava
  • ?Comenius University - Faculty of Mathematics, Physics and Informatics

Screenshot from the original paper

Screenshot from the original paper
Screenshot from the original paper

Strategy in a nutshell

The investment universe consists of 22 commodity futures, namely: soybean oil, corn, cocoa, cotton, feeder cattle, gold, copper, heating oil, coffee, live cattle, lean hogs, natural gas, oats, orange juice, palladium, platinum, soybean, sugar, silver, soybean meal, wheat, and crude oil. Firstly, at the beginning of each month, construct the asymmetry measure (IE) for each commodity based on the latest 260 daily returns using the following formula (the formula originally consists of theoretical density and integrals, however the solution is simple when empirical distribution is utilized): IE = (number of trading days when the daily return is greater than the average plus two standard deviations) – (number of trading days when the daily return is smaller than the average minus two standard deviations). Then rank the commodities according to their IE. Buy the bottom seven commodities with the lowest IE in the previous month and sell the top seven commodities with the highest IE in the previous month. Weigh the portfolio equally and rebalance monthly.

Economic rationale

A new measure IE that asymmetric strategy relies on uses the difference between upside and downside return probabilities to capture the degree of asymmetry. The greater the measure, the greater the upside potential of the asset return. Typical risk-averse investors prefer extreme gains and avoid extreme losses. Consequently, they bid up the prices of assets with a high chance of extreme gains and pay a lower price for assets with a high likelihood of extreme losses. As a result, the high (low) IE assets become overvalued (undervalued), and their subsequent returns are lower (higher). Therefore, the asymmetric strategy goes short on the most overvalued commodities with the highest IE and long on the most undervalued commodities with the lowest IE. Besides, the correlation analysis between the proposed strategy and the corresponding skewness portfolio indicates a low positive correlation with a correlation coefficient of 0.46. Even though the skewness and asymmetry effects are related, the correlation is not that high, and both effects form distinct trading strategies.

Backtest performance

Annualised return6.77%
Volatility9.43%
Beta0.28
Sharpe ratio0.74
Sortino ratio1.11
Maximum drawdown-19.09%
Win rate51%