# Introducing the Consistency-Weighted Return (CWR)

--

# An Alternative Measure for Evaluating Trading Strategies

## 1. Introduction

In the vast world of quantitative trading and investment metrics, the spotlight often falls on headline-grabbing returns. Strategies that boast huge profits can steal the show, but are they consistently reliable?

**Enter the Consistency-Weighted Return (CWR) — a metric designed to combine raw profit with the strategy’s consistency**.

## 2. What is the Consistency-Weighted Return (CWR)?

In the realm of trading and investing, **a consistent upward trajectory in profits is often considered the “ holy grail”**. This desired consistency is more than just a dream; it’s a reflection of stability and reliability in the chosen strategy.

The CWR offers an analytical approach to gauge the proximity of a trading strategy to this ideal scenario. It amalgamates the total returns of a strategy with its R² value, also known as the coefficient of determination.

But what exactly is this R² or coefficient of determination? In the discipline of statistics, when we aim to understand the linearity of a set of data points, we deploy a method called linear regression. Through this method, we strive to fit a line (often referred to as the “line of best fit”) amidst these points. The R² value emanates from this procedure. It quantifies the proportion of variance in the dependent variable (in this context, the returns from a trading strategy) that’s predictable from the independent variable(s). Essentially, an R² value of 1 denotes a perfect linear relationship, implying the data aligns flawlessly with the line of best fit. Conversely, a value nearing 0 suggests a lack of linearity, indicating that the data points are dispersed widely around the line.

Thus, the CWR isn’t merely a reflection of the absolute profits of a strategy. It poses a more intricate question: “While generating these profits, did the strategy maintain a semblance of uniformity and predictability?”

*Formula: CWR = R² × Annualized Returns*

Where:

**R² (Coefficient of Determination)**: It’s a metric of reliability. An indicator of how harmoniously the strategy’s returns adhered to a predictable, linear progression.

**Annualized Returns**: This is the sum of all the profits and losses for the period, scaled to represent a yearly return. Specifically, the formula for annualizing simple returns is:

*Annualized Returns = Total Returns × (duration in periods / periods per year)*

Where:

*Total Returns*: Total Returns is the cumulative sum of daily returns.*periods per year*: periods per year would be 365 for cryptocurrencies and 252 for most other assets.*duration in periods*: duration in periods is the total number of days in your time series.

## 3. Why CWR? The Advantage of Consistency

**Reliable Forecasts**: Strategies with a higher consistency are more predictable. They tend to exhibit fewer wild fluctuations, making them easier to manage and less stressful for traders.**Lower Risk**: Consistent returns can suggest that the strategy isn’t reliant on occasional big wins (which can be followed by big losses). This reduces the risk of severe drawdowns.**Long-Term Viability**: While ‘flash in the pan’ strategies might offer dramatic returns for a short period, they often fizzle out. Consistent strategies, on the other hand, can offer sustained performance.

## 4. Computing the Consistency-Weighted Return (CWR) with Python

To calculate R², the foundational step is to perform linear regression on our dataset to determine the best-fit line. An important distinction in our approach is that we opt not to use an intercept. This choice stems from the foundational principle that a profitability curve is inherently assumed to commence at zero.

In this section, we’ll explore two distinct methods to perform linear regression and subsequently derive the R² value:

**Ordinary Least Squares (OLS)**: A widely-used method in statistics, OLS is particularly suited for multivariate linear regression. In our analysis, we’ll employ the*statsmodels*package in Python to execute OLS.**Direct Calculation Method**: This method, designed for univariate linear regression, calculates the regression slope using the covariance between the variables and the variance of the independent variable. We’ll use this method to determine the R² value, differentiating between total variation and the variation explained by the regression model.

Let’s dive into both methods. Here’s a step-by-step guide using a hypothetical dataframe named `trades:`

1. Set up your environment:

`import numpy as np`

import pandas as pd

import matplotlib.pyplot as plt

import statsmodels.api as sm

2. Create a sample `trades`

dataframe:

`# Simulating a random but profitable pnl series`

bias = 0.25 # This constant ensures an upward trend on average

data = {

'time': pd.date_range(start='2023-06-01', periods=100, freq='D'),

'pnl': np.random.randn(100) + bias

}

trades = pd.DataFrame(data)

trades['pnl'] = trades['pnl'] / len(trades)

3. Calculate the Cumulative PnL and extract the ‘*X’ *and ‘*y’ *arrays:

`# Cumulative PnL.`

trades['cumpnl'] = trades['pnl'].cumsum()

# Create the X array based on the length of trades.

X = np.array(range(len(trades)))

# Set y to the 'cumpnl' column of trades.

y = trades['cumpnl'].values

4.1. Calculate R² using OLS:

`# Fit the OLS model without intercept.`

model = sm.OLS(y, X).fit()

# Extract R^2 directly from the model.

r_squared_ols = model.rsquared

4.2. Calculate R² using the direct method:

`# Calculate the beta coefficient without intercept.`

beta = np.sum(X * y) / np.sum(X**2)

# Predict y using the calculated beta.

y_pred_direct = beta * X

# Calculate R^2 directly for the model without intercept.

ss_tot_direct = np.sum(y**2)

ss_res_direct = np.sum((y - y_pred_direct)**2)

r_squared_direct = 1 - (ss_res_direct / ss_tot_direct)

5. Calculate CWR:

`# Parameters for annualization`

periods_per_year = 365 # Using 365 for assets like cryptocurrencies

# Calculate the duration in periods (days in this case, since 'trades' is a dataframe of daily data)

duration_in_periods = len(trades)

# Calculate the Annualized Returns

annualized_returns_ols = trades['cumpnl'].iloc[-1] * (periods_per_year / duration_in_periods)

annualized_returns_direct = trades['cumpnl'].iloc[-1] * (periods_per_year / duration_in_periods)

# Calculate the CWRs using the R^2 values and Annualized Returns

cwr_ols = annualized_returns_ols * r_squared_ols

cwr_direct = annualized_returns_direct * r_squared_direct

# Print the results for a quick comparison

print(f"CWR using OLS method: {cwr_ols:.6f}")

print(f"CWR using Direct Calculation method: {cwr_direct:.6f}")

`CWR using OLS method: 0.935333`

CWR using Direct Calculation method: 0.935333

6. Plot returns compared to the linear regressions:

`# Regression line for the OLS method:`

regression_line_ols = model.predict(X)

# Regression line for the Direct Calculation method:

regression_line_direct = beta * X

# Plotting

plt.figure(figsize=(12, 5))

# Plotting the actual cumulative PnL

plt.plot(trades['time'], trades['cumpnl'], label='Cumulative PnL', color='green')

# Plotting the regression line obtained from the OLS method

plt.plot(trades['time'], regression_line_ols, label='Regression (OLS)', color='black', linestyle='--')

# Plotting the regression line obtained from the Direct Calculation method

plt.plot(trades['time'], regression_line_direct, label='Regression (Direct)', color='grey', linestyle='--')

# Setting titles and labels

plt.title('Cumulative PnL compared with Regression Lines')

plt.xlabel('Time')

plt.ylabel('Cumulative PnL')

plt.legend()

plt.grid(True)

plt.tight_layout()

plt.show()

## 5. Flashy Returns vs. Steady Growth

Let’s illustrate the importance of CWR with a practical example.

**Imagine two trading strategies over a span of 100 days:**

- Strategy A: Characterized by volatile returns, this strategy often delivers significant gains followed by sharp drops, resembling a financial roller coaster. It’s worth noting that, by design, Strategy A ends up with a return that is 10% higher than Strategy B by the end of the period.
- Strategy B: This strategy emphasizes steadier growth. Although the returns are more consistent, there’s a touch of realistic noise, making it less predictable but far steadier than Strategy A.

`# Setting the seed for reproducibility`

np.random.seed(17)

# Strategy A: More volatile returns

volatile_returns = np.random.randn(100) * 0.1

# Strategy B: Steady returns but with slight noise

steady_returns = np.full(100, 0.005) + np.random.randn(100) * 0.01 # Adding noise to the steady return

# Adjusting to ensure it ends up 10% higher at the end of the period

cum_returns_A = volatile_returns.cumsum()

target_final_return = steady_returns.cumsum()[-1] + 0.10 # 10% higher than strategy B

scale_factor = target_final_return / cum_returns_A[-1]

volatile_returns *= scale_factor

# Creating DataFrames for both strategies

time_range = pd.date_range(start='2023-06-01', periods=100, freq='D')

trades_A = pd.DataFrame({'time': time_range, 'returns': volatile_returns})

trades_B = pd.DataFrame({'time': time_range, 'returns': steady_returns})

# Calculating the cumulative PnL for both strategies

trades_A['cumpnl'] = trades_A['returns'].cumsum()

trades_B['cumpnl'] = trades_B['returns'].cumsum()

# Plot

plt.figure(figsize=(12, 6))

plt.plot(trades_A['time'], trades_A['cumpnl'], label='Strategy A', color='red')

plt.plot(trades_B['time'], trades_B['cumpnl'], label='Strategy B', color='green')

plt.title('Comparison of Cumulative PnL: Flashy Returns vs. Steady Growth')

plt.xlabel('Time')

plt.ylabel('Cumulative PnL')

plt.legend()

plt.grid(True)

plt.tight_layout()

plt.show()

Despite Strategy A’s higher cumulative return by the end of the 100 days, their journeys differ significantly. When we evaluate these strategies with the CWR metric, a more revealing narrative emerges.

`# Parameters for annualization`

periods_per_year = 365 # Using 365 for assets like cryptocurrencies

# Calculate the duration in periods (days in this case, since trades_A and trades_B are dataframes of daily data)

duration_in_periods = len(trades_A) # Assuming trades_A and trades_B have the same duration

# Strategy A: CWR Calculation

X = np.arange(len(trades_A))

y_A = trades_A['cumpnl'].values

model_A = sm.OLS(y_A, X)

results_A = model_A.fit()

r_squared_A = results_A.rsquared

annualized_returns_A = trades_A['cumpnl'].iloc[-1] * (periods_per_year / duration_in_periods)

cwr_A = annualized_returns_A * r_squared_A

# Strategy B: CWR Calculation

y_B = trades_B['cumpnl'].values

model_B = sm.OLS(y_B, X)

results_B = model_B.fit()

r_squared_B = results_B.rsquared

annualized_returns_B = trades_B['cumpnl'].iloc[-1] * (periods_per_year / duration_in_periods)

cwr_B = annualized_returns_B * r_squared_B

# Printing the CWR for both strategies

print(f"CWR for Strategy A: {cwr_A:.4f}")

print(f"CWR for Strategy B: {cwr_B:.4f}")

`CWR for Strategy A: 1.6648`

CWR for Strategy B: 1.8462

CWR encapsulates not only the destination but also the journey taken. A strategy with more predictable and steady growth is likely to have a higher CWR, highlighting its consistent performance. This consistency can be more appealing to traders and investors since it reduces the unpredictability and potential stress of erratic performance swings.

Upon calculating the CWR for both strategies using the OLS method, Strategy B showcased a higher value, emphasizing its reliability despite having a lower overall return than Strategy A.

## 6. Incorporating CWR into Your Evaluation:

**Compare & Contrast**: Use CWR alongside other metrics like the Sharpe Ratio, Sortino Ratio, or Maximum Drawdown to get a more holistic view of a strategy’s performance.**Portfolio Construction**: For diversified portfolios, ensure you have a mix of strategies. Prioritize those with a higher CWR to bring more stability to your portfolio.**Risk Managemen**t: Recognize strategies with lower CWRs as they may introduce more volatility. Adjust your capital allocation accordingly.

## 7. Conclusion

While raw returns will always be a cornerstone of evaluating performance, the **Consistency-Weighted Return (CWR) offers a fresh perspective, emphasizing the importance of steady, reliable growth**. In the tumultuous seas of the trading world, a consistent strategy can be the anchor that ensures long-term success.

Remember, when exploring new metrics like CWR, it’s vital to understand their limitations and to use them in conjunction with other tools and insights. Good trading!