Results

Portfolio Margin Plots

Introduction

The portfolio margin chart is a stacked area chart where each color represents the proportion of your available margin that a security consumes over time.

Portfolio margin example with many stacked areas

Margin plots can appear counter intuitive and hard to understand. To simplify things, this chart only draws the top 5-10 holdings at a given point in time. It group the smaller holdings into an area called "Others".

Sample Frequency

In backtests, the chart areas are sampled once a day to produce the plot, so intraday margin usage isn't visible.

Cash vs Margin

When you trade with a margin account, orders consume your cash before borrowing anything on margin. This order of precedence minimizes the interest charges on your margin loans.

The following algorithm produces the following portfolio margin plot.

# region imports
from AlgorithmImports import *
# endregion

# Buy and hold an equal-weighted portfolio at 100% exposure.
class EqualWeightedFullExposureAlgorithm(QCAlgorithm):
    def initialize(self):
        self.set_start_date(2014, 1, 1)
        self.set_end_date(2016, 1, 1)
        self.set_cash(100000)
        self.symbols = [self.add_equity(t, Resolution.DAILY).symbol for t in ["SPY", "QQQ", "AAPL"]]

    def on_data(self, data: Slice):
        if not self.portfolio.invested:
            weight = 1.0 / len(self.symbols)
            for symbol in self.symbols:
                self.set_holdings(symbol, weight)

When the initial trades fill, the orders consume available cash before borrowing anything available from the margin. Consuming available cash first avoids interest charges on the margin-loan and avoids margin calls during market downturns. This results in a relatively steady line hovering on 50% as there is no free cash to purchase with, but only the remaining 50% margin.

Portfolio margin chart that stays fixed at 50%

The following algorithm is the same buy-and-hold strategy as the previous algorithm, but it only targets 50% exposure during the initial trades.

# region imports
from AlgorithmImports import *
# endregion

# Buy and hold an equal-weighted portfolio at 50% exposure.
class EqualWeightedHalfExposureAlgorithm(QCAlgorithm):
    def initialize(self):
        self.set_start_date(2014, 1, 1)
        self.set_end_date(2016, 1, 1)
        self.set_cash(100000)
        self.symbols = [self.add_equity(t, Resolution.DAILY).symbol for t in ["SPY", "QQQ", "AAPL"]]

    def on_data(self, data: Slice):
        if not self.portfolio.invested:
            weight = 0.5 / len(self.symbols)
            for symbol in self.symbols:
                self.set_holdings(symbol, weight)

This version only consumes 16% of the available margin for each security, leaving a substantial amount of cash free. As the portfolio value grows, the fraction of the margin that this cash represents shrinks, showing an upward trending plot as the holdings of AAPL grow in value.

Portfolio margin chart trending up throughout the backtest

Examples

The following examples provide insight into portfolio margin calculations and show the functionality of the portfolio margin chart.

Buying Put Options

The following algorithm demonstrates that when you buy a put Option contract and you long the underlying asset, it reduces the margin used by the underlying asset.

# region imports
from AlgorithmImports import *
# endregion

# Buy SPY, then buy a put Option to hedge and reduce margin usage.
class BuyingPutOptionsMarginAlgorithm(QCAlgorithm):
    def initialize(self):
        self.set_start_date(2013, 10, 7)
        self.set_end_date(2014, 7, 1)
        self.set_cash(100000)
        equity = self.add_equity("SPY", Resolution.MINUTE)
        self._equity_symbol = equity.symbol
        option = self.add_option("SPY", Resolution.MINUTE)
        option.set_filter(lambda u: u.strikes(-5, 5).expiration(150, 200))
        self._put_contract = None

    def on_data(self, data: Slice):
        # Buy SPY on the first trading day.
        if not self.portfolio[self._equity_symbol].invested:
            self.set_holdings(self._equity_symbol, 1)
            return

        # After a few months, buy a protective put.
        if self._put_contract is None and self.time > datetime(2014, 1, 1):
            for chain in data.option_chains.values():
                puts = [c for c in chain if c.right == OptionRight.PUT]
                if not puts:
                    continue
                contract = sorted(puts, key=lambda c: abs(c.strike - c.underlying_last_price))[0]
                self.market_order(contract.symbol, 1)
                self._put_contract = contract.symbol

In the following image, the gray area represents the underlying Equity and the blue area represents the put Option contract. The algorithm buys SPY on 10/07/2013, buys the put Option contract on 01/02/2014, and then exercises the contract on 06/21/2014.

Portfolio margin chart of hedging an SPY position by buying put Options

Buying Options Spreads

The following algorithm demonstrates that when you long and short two Option contracts at the same time, they combine together to reduce your margin usage.

# region imports
from AlgorithmImports import *
# endregion

class HyperActiveOrangeHippopotamus(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2023, 2, 3)
        self.set_end_date(2023, 3, 3)
        self.set_cash(100000)
        option = self.add_option("GOOG", Resolution.MINUTE)
        self.symbol = option.symbol
        option.set_filter(lambda universe: universe.strikes(-10, 10).expiration(30, 45))

        self.itm_call = None
        self.otm_call = None

    def on_data(self, data: Slice):
        # Get the OptionChain.
        chain = data.option_chains.get(self.symbol, None)
        if not chain: return

        if self.itm_call is None:
            # Get the furthest expiry date of the contracts.
            expiry = sorted(chain, key = lambda x: x.expiry, reverse=True)[0].expiry
            # Select the ITM call with the lowest strike.
            itm_calls = [c for c in chain if c.expiry == expiry and c.right == OptionRight.CALL and c.strike < chain.underlying.price]
            if len(itm_calls) == 0: return
            self.itm_call = sorted(itm_calls, key=lambda x: x.strike)[0]
            # Sell an ITM call.
            self.market_order(self.itm_call.symbol, -1)

        if self.otm_call is None and self.time.day > 13:
            # Select the OTM call with the highest strike.
            otm_calls = [c for c in chain if c.expiry == self.itm_call.expiry and c.right == OptionRight.CALL and c.strike > chain.underlying.price and c.strike > self.itm_call.strike]
            if len(otm_calls) == 0: return
            self.otm_call = sorted(otm_calls, key=lambda x: x.strike)[-1]
            # Buy an OTM call.
            self.market_order(self.otm_call.symbol, 1)

In the following image, the blue area represents a short position in a call Option contract and the gray area represents a long position in a call Option contract. The algorithm forms a bear call spread one leg at a time, shorting an in-the-money contract on 02/03/2023 and then longing an out-of-the-money contract on 02/14/2023.

Portfolio margin chart of stepping into a bear class spread on leg at a time

Buying Futures

The following algorithm demonstrates that Futures have a fixed, USD-denominated margin requirement.

# region imports
from AlgorithmImports import *
# endregion

class SleepyTanAlligator(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2023, 10, 1)
        self.set_end_date(2023, 11, 1)
        self.set_cash(100000)
        self.future = self.add_future(Futures.Indices.SP_500_E_MINI)
        self.symbol = self.future.symbol
        self.future.set_filter(0, 182)

    def on_data(self, data: Slice):
        if not self.portfolio.invested:
            for continuous_contract_symbol, chain in data.futures_chains.items():
                contract = sorted(chain, key=lambda contract: contract.open_interest, reverse=True)[0]
                self.market_order(contract.symbol, 1)

    def on_end_of_day(self, symbol) -> None:
        self.plot("Margin", "MarginRemaining", self.portfolio.margin_remaining)
        self.plot("Margin", "TotalMarginUsed", self.portfolio.total_margin_used)

In the following image, the blue area represents the percentage of the available margin that the Futures contract uses. The algorithm buys the contract on 10/02/2023 and holds it until the end of the backtest. As the total portfolio value decreases during the last two weeks in the backtest, the amount of available margin in the portfolio decreases. However, the USD amount of margin used by the Futures contract is constant, so the proportion of portfolio margin used by the contract increases at the same time.

Portfolio margin chart of buying a futures contract

Buying Forex

The following algorithm demonstrates how the 50x leverage available for Forex impacts the portfolio margin chart.

# region imports
from AlgorithmImports import *
# endregion

# Buy EURUSD at 500% exposure to demonstrate Forex margin usage.
class ForexMarginAlgorithm(QCAlgorithm):
    def initialize(self):
        self.set_start_date(2018, 1, 1)
        self.set_end_date(2018, 10, 1)
        self.set_cash(100000)
        self._symbol = self.add_forex("EURUSD", Resolution.DAILY, Market.OANDA).symbol

    def on_data(self, data: Slice):
        if not self.portfolio.invested:
            self.set_holdings(self._symbol, 5)

In the following image, the blue area represents the percentage of the available margin that the Forex position uses. The algorithm buys 500% exposure to the EURUSD pair on 01/02/2018 and holds it until the end of the backtest. The portfolio margin chart shows that only 10% of the available margin is used to enter the trade since 500% / 50 = 10%.

Portfolio margin chart of buying a forex pair with 10% margin

Series Color Changes

The portfolio margin plot updates daily as the backtest runs. However, the color of a specific security in the plot isn't finalized until the end of the backtest. For instance, the following image shows an example plot during execution:

Stacked area chart where the colors are not consistent over time

The following image shows what the plot looks like at the end of the backtest:

Stacked area chart where the colors are consistent over time

Series Names

The series names in the chart are the last known tickers for the respective assets (for example, SPY and XLE). Therefore, if you trade multiple assets throughout a backtest that share the same last known ticker (for example, FB in 1998 and FB in 2014), the chart labels them under the same series name.

You can also see our Videos. You can also get in touch with us via Discord.

Did you find this page helpful?

Contribute to the documentation: