Hi,
I've been trying to implement Chande Momemtum Oscillator indicator in QC platform and I noticed that the cmo data provided was different from Tradingview.
On closer inspection and observing the C# codes, I noticed there's 1 error in the C# code that may have contributed to the error/discrepency.
Both the CMO data provided by TA & Tradingview matched and implies there's no inherent database differences.
There's another Forum thread also found CMO discrepency reported previously:
https://www.quantconnect.com/forum/discussion/12510/discrepancy-between-quantconnect-and-tradingview/p1
I think the error came from the C# code starting Line 84:
C# source code: https://github.com/QuantConnect/Lean/blob/master/Indicators/ChandeMomentumOscillator.cs
******************************
if (difference < 0)
_prevLoss -= difference;
else
_prevGain += difference;
******************************
The above if statement do not update both _prevLoss AND _prevGain, it only EITHER update _prevLoss OR _preGain. This explains the discrepency.
Lucas Chan
Response from support but I respectfully disagree that the calculation is the “same” as per the logic.
***************************************************************************************************************
Hi Lucas,
Here is TA-LIB implementation for the difference < 0 if-condition statement:
https://github.com/TA-Lib/ta-lib/blob/main/src/ta_func/ta_CMO.c#L287
https://github.com/TA-Lib/ta-lib/blob/main/src/ta_func/ta_CMO.c#L330
The calculation is the same.
QuantConnect/Lean validates its indicators with external data. Here is the file we use for CMO:
https://github.com/QuantConnect/Lean/blob/master/Tests/TestData/spy_cmo.txt
If you use this data (the close, 5th column) to calculate CMO with TA-LIB it should return the same results (the 7th column).
Best regards,
Alex
*********************************************************************************************************************************
QC CMO implementation:
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Indicators
{
/// <summary>
/// This indicator computes the Chande Momentum Oscillator (CMO).
/// CMO calculation is mostly identical to RSI.
/// The only difference is in the last step of calculation:
/// RSI = gain / (gain+loss)
/// CMO = (gain-loss) / (gain+loss)
/// </summary>
public class ChandeMomentumOscillator : WindowIndicator<IndicatorDataPoint>, IIndicatorWarmUpPeriodProvider
{
private decimal _prevValue;
private decimal _prevGain;
private decimal _prevLoss;
/// <summary>
/// Initializes a new instance of the <see cref="ChandeMomentumOscillator"/> class using the specified period.
/// </summary>
/// <param name="period">The period of the indicator</param>
public ChandeMomentumOscillator(int period)
: this($"CMO({period})", period)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ChandeMomentumOscillator"/> class using the specified name and period.
/// </summary>
/// <param name="name">The name of this indicator</param>
/// <param name="period">The period of the indicator</param>
public ChandeMomentumOscillator(string name, int period)
: base(name, period)
{
}
/// <summary>
/// Gets a flag indicating when this indicator is ready and fully initialized
/// </summary>
public override bool IsReady => Samples > Period;
/// <summary>
/// Required period, in data points, for the indicator to be ready and fully initialized.
/// </summary>
public int WarmUpPeriod => 1 + Period;
/// <summary>
/// Computes the next value of this indicator from the given state
/// </summary>
/// <param name="input">The input given to the indicator</param>
/// <param name="window">The window for the input history</param>
/// <returns>A new value for this indicator</returns>
protected override decimal ComputeNextValue(IReadOnlyWindow<IndicatorDataPoint> window, IndicatorDataPoint input)
{
if (Samples == 1)
{
_prevValue = input.Value;
return 0m;
}
var difference = input.Value - _prevValue;
_prevValue = input.Value;
if (Samples > Period + 1)
{
_prevLoss *= (Period - 1);
_prevGain *= (Period - 1);
}
if (difference < 0)
_prevLoss -= difference;
else
_prevGain += difference;
if (!IsReady)
return 0m;
_prevLoss /= Period;
_prevGain /= Period;
var sum = _prevGain + _prevLoss;
return sum != 0 ? 100m * ((_prevGain - _prevLoss) / sum) : 0m;
}
/// <summary>
/// Resets this indicator to its initial state
/// </summary>
public override void Reset()
{
_prevValue = 0;
_prevGain = 0;
_prevLoss = 0;
base.Reset();
}
}
}
Jared Broad
Thank you for the thoughtful comments. Can you share the TV pine script and asset you were testing with, and we can see if we can repeat the difference you see?
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
.ekz.
Ran into same issue and ended up using GPT to port TV's Pinescript version to a QC LEAN custom indicator (Python). The custom one has a similar profile to TV's, but not a perfect match, given the way TV handles data.
Sharing the code below in case it helps others.
Also, Jared Broad : I generated a ChatGPT thread advising on how we might update the LEAN logic to match TV's
(assuming we want to do that). Here's the link. TLDR: we should consider implementing separate
accumulators for positive and negative changes within the period instead of adjusting _preGain and _prevLoss over
time.
Python Version
Custom QC Version
Visual Comparison
imgur[dot]com/
(Cant attach image, and pasting the link seems to break the post)
Lucas Chan
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!