Minimum Profit Strategy

Introduction

This trading strategy takes new spread values one by one and allows checking if the conditions to open a position are fulfilled with each new timestamp and value provided. This allows for easier integration of these strategies into an existing data pipeline. Also, the strategy object keeps track of open and closed trades and the supporting information related to them.

Minimum Profit Strategy

The Minimum Profit class from the Cointegration Approach generates optimal entry and exit levels (buy_level, close_level, sell_level) as well as number of shares (shares_A, shares_B) to trade per asset in a cointegrated pair. The strategy described in this section of the documentation can be used to trade those generated signals.

The signal generation process relies on the performance of the spread, which is calculated as:

\[Spread = PriceA + beta * PriceB\]

Where the \(beta\) parameter is given by the Minimum Profit class. If the spread value drops below the buy_level, a buy trade should be opened, meaning we should go shares_A long and shares_B short. If the spread value rises above the sell_level, a sell trade should be opened, so we short shares_B and go long shares_A. Upon reaching the close_level, a buy or a sell trade should be closed. This strategy assumes only one open trade at a time.

../_images/AME-DOV1.png

An example of pair trading Ametek Inc. (AME) and Dover Corp. (DOV) from January 2nd, 2019 to date. The green line is the sell level, the red line is the buy level, and the black line is the close level.

The strategy object is initialized with a number of shares and optimal levels.

The update_spread_value method allows adding new spread values one by one - when they are available. At each stage, the check_entry_signal method checks if the trade should be entered according to the above-described logic. If the trade should be opened, it can be added to the internal dictionary using the add_trade method.

As well, the update_trades method can be used to check if any trades should be closed. If so, the internal dictionaries are updated, and the list of the closed trades at this stage is returned.

Implementation

class MinimumProfitTradingRule(shares: array, optimal_levels: array, spread_window: int = 10)

This class implements trading strategy from the Minimum Profit Optimization method from the paper by Lin, Y.-X., McCrae, M., and Gulati, C. “Loss protection in pairs trading through minimum profit bounds: a cointegration approach”

The strategy generates a signal by when spread <= buy_level or spread => sell_level and exit from a position when |spread| <= |close_level|.

This strategy allows only one open trade at a time.

__init__(shares: array, optimal_levels: array, spread_window: int = 10)

Class constructor.

Parameters:
  • shares – (np.array) Number of shared to trade per leg in the cointegration pair.

  • optimal_levels – (np.array) Optimal levels to enter aenter and close a trade.

  • spread_window – (int) Number of previous spread values to print when reporting trades.

add_trade(start_timestamp: Timestamp, side_prediction: int, uuid: UUID | None = None, shares: array | None = None)

Adds a new trade to track. Calculates trigger prices and trigger timestamp.

Parameters:
  • start_timestamp – (pd.Timestamp) Timestamp of the future label.

  • side_prediction – (int) External prediction for the future label.

  • uuid – (str) Unique identifier used to link label to tradelog action.

  • shares – (np.array) Number of shares bought and sold per asset.

check_entry_signal() tuple

Function which checks entry condition for a spread series based on self.entry_buy_signal, self.entry_sell_signal, self.spread_series.

Returns:

(tuple) Tuple of boolean entry flag and side (if entry flag is True).

update_spread_value(latest_spread_value: float)

Adds latest spread value to self.spread_series. Once it is done - one can check entry/exit signals.

Parameters:

latest_spread_value – (float) Latest spread value.

update_trades(update_timestamp: Timestamp) list

Checks whether any of the thresholds are triggered and currently open trades should be closed.

Parameters:

update_timestamp – (pd.Timestamp) New timestamp to check vertical threshold.

Returns:

(list) List of closed trades.

Example

# Importing packages
import pandas as pd
import numpy as np

# Importing ArbitrageLab tools
from arbitragelab.cointegration_approach.minimum_profit import MinimumProfit
from arbitragelab.trading import MinimumProfitTradingRule

# Using MinimumProfit as optimizer ...

# Generate optimal trading levels and number of shares to trade
num_of_shares, optimal_levels = optimizer.get_optimal_levels(optimal_ub,
                                                             minimum_profit,
                                                             beta_eg,
                                                             epsilon_t_eg)

# Calculating spread
spread = optimizer.construct_spread(data, beta_eg)

# Creating a strategy
strategy = MinimumProfitTradingRule(num_of_shares, optimal_levels)

# Adding initial spread value
strategy.update_spread_value(spread[0])

# Feeding spread values to the strategy one by one
for time, value in spread.iteritems():
    strategy.update_spread_value(value)

    # Checking if logic for opening a trade is triggered
    trade, side = strategy.check_entry_signal()

    # Adding a trade if we decide to trade signal
    if trade:
        strategy.add_trade(start_timestamp=time, side_prediction=side)

    # Update trades, close if logic is triggered
    close = strategy.update_trades(update_timestamp=time)

# Checking currently open trades
open_trades = strategy.open_trades

# Checking all closed trades
closed_trades = strategy.closed_trades

Research Notebooks

The following research notebook can be used to better understand the Strategy described above.

References