| Overall Statistics |
|
Total Orders 2399 Average Win 0.59% Average Loss -0.39% Compounding Annual Return 14.649% Drawdown 25.900% Expectancy 0.436 Start Equity 100000 End Equity 827028.97 Net Profit 727.029% Sharpe Ratio 0.68 Sortino Ratio 0.792 Probabilistic Sharpe Ratio 11.796% Loss Rate 43% Win Rate 57% Profit-Loss Ratio 1.51 Alpha 0.041 Beta 0.608 Annual Standard Deviation 0.139 Annual Variance 0.019 Information Ratio 0.057 Tracking Error 0.123 Treynor Ratio 0.156 Total Fees $7349.40 Estimated Strategy Capacity $34000000.00 Lowest Capacity Asset GM URPX6IW2RK2T Portfolio Turnover 4.76% |
#region imports
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.Drawing;
using QuantConnect;
using QuantConnect.Algorithm.Framework;
using QuantConnect.Algorithm.Framework.Selection;
using QuantConnect.Algorithm.Framework.Alphas;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Algorithm.Framework.Execution;
using QuantConnect.Algorithm.Framework.Risk;
using QuantConnect.Parameters;
using QuantConnect.Benchmarks;
using QuantConnect.Brokerages;
using QuantConnect.Util;
using QuantConnect.Interfaces;
using QuantConnect.Algorithm;
using QuantConnect.Indicators;
using QuantConnect.Data;
using QuantConnect.Data.Consolidators;
using QuantConnect.Data.Custom;
using QuantConnect.DataSource;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Notifications;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.Fills;
using QuantConnect.Orders.Slippage;
using QuantConnect.Scheduling;
using QuantConnect.Securities;
using QuantConnect.Securities.Equity;
using QuantConnect.Securities.Future;
using QuantConnect.Securities.Option;
using QuantConnect.Securities.Forex;
using QuantConnect.Securities.Crypto;
using QuantConnect.Securities.Interfaces;
using QuantConnect.Storage;
using QuantConnect.Data.Custom.AlphaStreams;
using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
using MathNet.Numerics;
using MathNet.Numerics.LinearAlgebra;
#endregion
//Copied from this forum:
//href https://www.quantconnect.com/forum/discussion/695/adjusted-slope--exponential-slope----annualized-slope--r-squuared--adjusted-slope/p1
namespace QuantConnect.Algorithm.CSharp.Helpers
{
public class AnnualizedExponentialSlopeIndicator : WindowIndicator<IndicatorDataPoint>
{
/// <summary>
/// Array representing the time.
/// </summary>
private readonly double[] t;
public AnnualizedExponentialSlopeIndicator(int period)
: base("AESI(" + period + ")", period)
{
t = Vector<double>.Build.Dense(period, i => i + 1).ToArray();
}
public AnnualizedExponentialSlopeIndicator(string name, int period)
: base(name, period)
{
t = Vector<double>.Build.Dense(period, i => i + 1).ToArray();
}
protected override decimal ComputeNextValue(IReadOnlyWindow<IndicatorDataPoint> window, IndicatorDataPoint input)
{
// Until the window is ready, the indicator returns the input value.
if (window.Samples <= window.Size) return 0m;
// Sort the window by time, convert the observations to double and transform it to an array
var series = window
.OrderBy(i => i.Time)
.Select(i => Convert.ToDouble(Math.Log(Convert.ToDouble(i.Value))))
.ToArray();
// Fit OLS
// solves y=a + b*x via linear regression
// http://numerics.mathdotnet.com/Regression.html
var ols = Fit.Line(x: t, y: series);
var intercept = ols.Item1;
var slope = ols.Item2;
// compute rsquared
var rsquared = GoodnessOfFit.RSquared(t.Select(x => intercept + slope * x), series);
// anything this small can be viewed as flat
if (double.IsNaN(slope) || Math.Abs(slope) < 1e-25) return 0m;
// trading days per year for us equities
const int dayCount = 252;
// annualize dy/dt
var annualSlope = ((Math.Pow(Math.Exp(slope), dayCount)) - 1) * 100;
// scale with rsquared
annualSlope = annualSlope * rsquared;
if (annualSlope >= (double)decimal.MaxValue || annualSlope <= (double)decimal.MinValue)
{
annualSlope = 0;
}
return Convert.ToDecimal(annualSlope);
}
}
}#region imports
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.Drawing;
using QuantConnect;
using QuantConnect.Algorithm.Framework;
using QuantConnect.Algorithm.Framework.Selection;
using QuantConnect.Algorithm.Framework.Alphas;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Algorithm.Framework.Execution;
using QuantConnect.Algorithm.Framework.Risk;
using QuantConnect.Parameters;
using QuantConnect.Benchmarks;
using QuantConnect.Brokerages;
using QuantConnect.Util;
using QuantConnect.Interfaces;
using QuantConnect.Algorithm;
using QuantConnect.Indicators;
using QuantConnect.Data;
using QuantConnect.Data.Consolidators;
using QuantConnect.Data.Custom;
using QuantConnect.DataSource;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Notifications;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.Fills;
using QuantConnect.Orders.Slippage;
using QuantConnect.Scheduling;
using QuantConnect.Securities;
using QuantConnect.Securities.Equity;
using QuantConnect.Securities.Future;
using QuantConnect.Securities.Option;
using QuantConnect.Securities.Forex;
using QuantConnect.Securities.Crypto;
using QuantConnect.Securities.Interfaces;
using QuantConnect.Storage;
using QuantConnect.Data.Custom.AlphaStreams;
using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
#endregion
//Copied from this forum:
//href https://www.quantconnect.com/forum/discussion/695/adjusted-slope--exponential-slope----annualized-slope--r-squuared--adjusted-slope/p1
namespace QuantConnect.Algorithm.CSharp.Helpers
{
public class CustomMomentumIndicator : TradeBarIndicator
{
private Symbol _symbol;
private int _windowSize;
public readonly AnnualizedExponentialSlopeIndicator AnnualizedSlope;
public readonly ExponentialMovingAverage MovingAverage;
public readonly GapIndicator Gap;
public readonly AverageTrueRange Atr;
public CustomMomentumIndicator(Symbol symbol, int annualizedSlopeWindow, int movingAverageWindow, int gapWindow, int atrWindow) : base($"CMI({symbol}, {annualizedSlopeWindow}, {movingAverageWindow}, {gapWindow})")
{
_symbol = symbol;
AnnualizedSlope = new AnnualizedExponentialSlopeIndicator(annualizedSlopeWindow);
MovingAverage = new ExponentialMovingAverage(movingAverageWindow);
Gap = new GapIndicator(gapWindow);
Atr = new AverageTrueRange(atrWindow);
_windowSize = (new int[] { movingAverageWindow, annualizedSlopeWindow, gapWindow, atrWindow }).Max();
}
public Symbol Symbol { get { return _symbol; } }
public override void Reset()
{
AnnualizedSlope.Reset();
MovingAverage.Reset();
Gap.Reset();
Atr.Reset();
base.Reset();
}
protected override decimal ComputeNextValue(TradeBar input)
{
AnnualizedSlope.Update(input.EndTime, input.Value);
MovingAverage.Update(input.EndTime, input.Value);
Gap.Update(input.EndTime, input.Value);
Atr.Update(input);
return AnnualizedSlope;
}
/// <summary>
/// Are the indicators ready to be used?
/// </summary>
public override bool IsReady
{
get { return AnnualizedSlope.IsReady && MovingAverage.IsReady && Gap.IsReady && Atr.IsReady; }
}
/// <summary>
/// Returns the Window of the indicator required to warm up indicator
/// </summary>
public int Window
{
get {return _windowSize;}
}
public new string ToDetailedString()
{
return $"Symbol:{_symbol} Slope:{AnnualizedSlope.ToDetailedString()} Average:{MovingAverage.ToDetailedString()} Gap:{Gap.ToDetailedString()} Atr:{Atr.ToDetailedString()} IsReady:{IsReady}";
}
}
}#region imports
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.Drawing;
using QuantConnect;
using QuantConnect.Algorithm.Framework;
using QuantConnect.Algorithm.Framework.Selection;
using QuantConnect.Algorithm.Framework.Alphas;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Algorithm.Framework.Execution;
using QuantConnect.Algorithm.Framework.Risk;
using QuantConnect.Parameters;
using QuantConnect.Benchmarks;
using QuantConnect.Brokerages;
using QuantConnect.Util;
using QuantConnect.Interfaces;
using QuantConnect.Algorithm;
using QuantConnect.Indicators;
using QuantConnect.Data;
using QuantConnect.Data.Consolidators;
using QuantConnect.Data.Custom;
using QuantConnect.DataSource;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Notifications;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.Fills;
using QuantConnect.Orders.Slippage;
using QuantConnect.Scheduling;
using QuantConnect.Securities;
using QuantConnect.Securities.Equity;
using QuantConnect.Securities.Future;
using QuantConnect.Securities.Option;
using QuantConnect.Securities.Forex;
using QuantConnect.Securities.Crypto;
using QuantConnect.Securities.Interfaces;
using QuantConnect.Storage;
using QuantConnect.Data.Custom.AlphaStreams;
using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
using MathNet.Numerics;
using MathNet.Numerics.Statistics;
#endregion
/// <summary>
/// Indicator to indicate the percentage (0.10 = 10%) of which a security gapped over the last period;
/// </summary>
namespace QuantConnect.Algorithm.CSharp.Helpers
{
public class GapIndicator : WindowIndicator<IndicatorDataPoint>
{
public GapIndicator(int period)
: base("GAP(" + period + ")", period)
{
}
public GapIndicator(string name, int period)
: base(name, period)
{
}
public override bool IsReady
{
get { return Samples >= Period; }
}
protected override decimal ComputeNextValue(IReadOnlyWindow<IndicatorDataPoint> window, IndicatorDataPoint input)
{
if (window.Count < 3) return 0m;
var diff = new double[window.Count];
// load input data for regression
for (int i = 0; i < window.Count - 1; i++)
{
diff[i] = (double)((window[i + 1] - window[i]) / (window[i] == 0 ? 1 : window[i].Value));
}
return (decimal) diff.MaximumAbsolute();
}
}
}#region imports
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.Drawing;
using QuantConnect;
using QuantConnect.Algorithm.Framework;
using QuantConnect.Algorithm.Framework.Selection;
using QuantConnect.Algorithm.Framework.Alphas;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Algorithm.Framework.Execution;
using QuantConnect.Algorithm.Framework.Risk;
using QuantConnect.Parameters;
using QuantConnect.Benchmarks;
using QuantConnect.Brokerages;
using QuantConnect.Util;
using QuantConnect.Interfaces;
using QuantConnect.Algorithm;
using QuantConnect.Indicators;
using QuantConnect.Data;
using QuantConnect.Data.Consolidators;
using QuantConnect.Data.Custom;
using QuantConnect.DataSource;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Notifications;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.Fills;
using QuantConnect.Orders.Slippage;
using QuantConnect.Scheduling;
using QuantConnect.Securities;
using QuantConnect.Securities.Equity;
using QuantConnect.Securities.Future;
using QuantConnect.Securities.Option;
using QuantConnect.Securities.Forex;
using QuantConnect.Securities.Crypto;
using QuantConnect.Securities.Interfaces;
using QuantConnect.Storage;
using QuantConnect.Data.Custom.AlphaStreams;
using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
#endregion
namespace QuantConnect.Algorithm.CSharp
{
public class MarketRegimeFilter
{
private SimpleMovingAverage _spyMovingAverage200;
private RateOfChange _bilROC21;
private RateOfChange _iefROC21;
public MarketRegimeFilter(SimpleMovingAverage spyMovingAverage200, RateOfChange bilROC21, RateOfChange iefROC21)
{
_spyMovingAverage200 = spyMovingAverage200;
_bilROC21 = bilROC21;
_iefROC21 = iefROC21;
}
// Checks if the SPY is over its 200d SMA
public bool RiskON(decimal spyPrice){
bool riskonSPY = false;
if (spyPrice > _spyMovingAverage200){
riskonSPY = true;
}
if (riskonSPY)
return true;
else
return false;
}
// Checks if the iefROC21 is over its bilROC21 and is over 0.
public bool TradeBonds(){
bool tradeBonds = false;
if (_iefROC21 > _bilROC21 && _iefROC21 > 0){
tradeBonds = true;
}
if (tradeBonds)
return true;
else
return false;
}
}
}
#region imports
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.Drawing;
using QuantConnect;
using QuantConnect.Algorithm.Framework;
using QuantConnect.Algorithm.Framework.Selection;
using QuantConnect.Algorithm.Framework.Alphas;
using QuantConnect.Algorithm.Framework.Portfolio;
using QuantConnect.Algorithm.Framework.Execution;
using QuantConnect.Algorithm.Framework.Risk;
using QuantConnect.Parameters;
using QuantConnect.Benchmarks;
using QuantConnect.Brokerages;
using QuantConnect.Util;
using QuantConnect.Interfaces;
using QuantConnect.Algorithm;
using QuantConnect.Algorithm.CSharp.Helpers;
using QuantConnect.Indicators;
using QuantConnect.Data;
using QuantConnect.Data.Consolidators;
using QuantConnect.Data.Custom;
using QuantConnect.DataSource;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Notifications;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.Fills;
using QuantConnect.Orders.Slippage;
using QuantConnect.Scheduling;
using QuantConnect.Securities;
using QuantConnect.Securities.Equity;
using QuantConnect.Securities.Future;
using QuantConnect.Securities.Option;
using QuantConnect.Securities.Forex;
using QuantConnect.Securities.Crypto;
using QuantConnect.Securities.Interfaces;
using QuantConnect.Storage;
using QuantConnect.Data.Custom.AlphaStreams;
using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm;
using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm;
#endregion
namespace QuantConnect.Algorithm.CSharp
{
/*
*******************************************************************************************
2023/01/15 - Updated and Adapted by CabedoVestement based on the previous work of
Quant Trader (https://www.quantconnect.com/forum/discussion/3136/andreas-f-clenow-momentum)
*******************************************************************************************
Momentum strategy according to Andreas F. Clenow book
'Stocks on the Move - Beating the Market with Hedge Fund Momentum Strategies'
** Objective of the algorithm: **
The algorithm's objective is to select stocks from the SP500 universe with the fastest rising prices while maintaining limited volatility.
** Key Trading Rules: **
Market Universe: It operates within the SP500 universe.
Trading Schedule: Trades are executed on Wednesday
Regular Selling/Buying: Stocks are bought and sold every week.
Rebalancing: Positions are rebalanced twice a month to manage risk.
Top Ranking Stocks: The algorithm selects the top 100 (or 20%) ranked stocks from the SP500.
Moving Average Filter: Stocks below their 100-day moving average are sold.
Gap Filter: Stocks with a gap of more than 15% over the last 90 days are not bought.
Index Membership Filter: Stocks that leave the SP500 index are sold.
Market Regime Filter: Stocks are bought only when the SP500 is above its 200-day moving average.
Position Sizing: Position sizes are calculated based on a 0.1% risk of the total portfolio value using the Average True Range (ATR) of the last 20 days.
Momentum Calculation: Momentum is calculated based on the annualized exponential regression slope over the past 90 days.
Momentum Weighting: Momentum values are weighted for volatility adjustment using the coefficient of determination (R-squared).
*/
/// Momentum strategy according to Andreas F. Clenow book titled
/// 'Stocks on the Move: Beating the Market with Hedge Fund Momentum Strategies'
public class StocksOnTheMoveAlgorithm : QCAlgorithm
{
///Momentum is calculated based on 90 past days annualized exponential regression slope;
private int _annualizedSlopeWindow = 90;
/// If the stock is below its 100 days moving average, sell it;
private int _movingAverageWindow = 100;
/// ATR window
private int _atrWindow = 20;
/// Daily Risk of each trade on the portfolio (0,5%)
private const decimal RiskPerContractOnPortfolio = 0.005m;
/// Total number of security symbols in the Universe
private static int _universeSelectMaxStocks = 100;
/// Holds our security custom indicators per symbol
private Dictionary<Symbol, CustomMomentumIndicator> _customIndicators = new Dictionary<QuantConnect.Symbol, CustomMomentumIndicator>(_universeSelectMaxStocks);
// If the SP500 is above the 200 days moving average we buy stocks, otherwise not;
private MarketRegimeFilter _marketRegimeFilter;
//If the stock is not in the top 100/ 20% ranking, sell it;
private int _topNStockOfSp500 = 20;
///If the stock gapped > 15% over period (90d) Do not buy: Maximum Gap in percentage
private decimal _maximumGap = 0.15m;
private int _gapWindow = 90;
///Minimum annualized slope before buying stock.
private decimal _minimumAnnualizedSlope = 0m;
///Twice a month rebalance the positions sizes (risk);
private bool _rebalanceWeek = false;
public bool RebalanceWeek { get { return _rebalanceWeek; } }
///Broker fee to take into account to check if Cash is avalaible
private const decimal BrokerFee = 0.005m;
// Debug parameters
private bool _isLogging = false;
/// Is debugging set?
public bool IsLooging { get { return _isLogging; } }
public new void Log(string message)
{
if (IsLooging)
base.Log(message);
}
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm.
public override void Initialize()
{
_isLogging = false;
//Set trading window
SetStartDate(2009, 1, 1);
SetEndDate(DateTime.Now);
//Set cash and brokermodel
SetCash(100000);
SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);
//Set Benchmark
Security security = AddEquity("SPY", Resolution.Daily);
SetBenchmark(security.Symbol);
security = AddEquity("BIL", Resolution.Daily);
security = AddEquity("IEF", Resolution.Daily);
// Set the MarketRegimeFilter
SimpleMovingAverage spyMovingAverage200 = SMA("SPY", 10, Resolution.Daily);
// Set Bonds ROC
RateOfChange bilROC21 = ROC("BIL",21,Resolution.Daily);
RateOfChange iefROC21 = ROC("IEF",21,Resolution.Daily);
//Warm up SMA
SetWarmUp(200);
IEnumerable<TradeBar> history = History("SPY", 10, Resolution.Daily);
foreach (TradeBar tradeBar in history)
{
spyMovingAverage200.Update(tradeBar.EndTime, tradeBar.Close);
}
history = History("BIL", 21, Resolution.Daily);
foreach (TradeBar tradeBar in history)
{
bilROC21.Update(tradeBar.EndTime, tradeBar.Close);
}
history = History("IEF", 21, Resolution.Daily);
foreach (TradeBar tradeBar in history)
{
iefROC21.Update(tradeBar.EndTime, tradeBar.Close);
}
_marketRegimeFilter = new MarketRegimeFilter(spyMovingAverage200,bilROC21,iefROC21);
//Setup universe based on ETF: https://www.quantconnect.com/docs#Universes
UniverseSettings.Resolution = Resolution.Daily;
AddUniverse(Universe.ETF("OEF", Market.USA, UniverseSettings));
//Trade only on Wednesday at opening after 1 minutes
Schedule.On(DateRules.Every(DayOfWeek.Thursday),
TimeRules.AfterMarketOpen("SPY", 1), ScheduledOnWednesday1MinuteAfterMarketOpen);
}
// SECURITY RANKING, SELL, REBALANCE AND BUY
private void ScheduledOnWednesday1MinuteAfterMarketOpen()
{
if (IsWarmingUp) return;
// First, we order by slope and we take top 20% ranked
var sortedEquityListBySlope = _customIndicators.Where(x => x.Value.IsReady)
.OrderByDescending(x => x.Value.AnnualizedSlope)
.Take(_topNStockOfSp500)
.ToList();
// Second, we filter by minimum slope, above moving average and max gap
sortedEquityListBySlope = sortedEquityListBySlope
.Where(x => x.Value.AnnualizedSlope > _minimumAnnualizedSlope
&& Securities[x.Key].Price > x.Value.MovingAverage
&& x.Value.Gap < _maximumGap).ToList();
//Sell if security is not in list
foreach (var security in Portfolio.Values.Where(x => x.Invested))
{
var symbolHold = security.Symbol;
if (!sortedEquityListBySlope.Exists(x => x.Value.Symbol == symbolHold))
{
Liquidate(symbolHold);
}
}
bool riskON = _marketRegimeFilter.RiskON(Securities["SPY"].Price);
//Twice a month rebalance the positions sizes (risk);
if (RebalanceWeek) {
_rebalanceWeek = false;
var risk = Portfolio.TotalPortfolioValue * RiskPerContractOnPortfolio;
foreach (var security in Portfolio.Values.Where(x => x.Invested))
{
var symbolHold = security.Symbol;
var quantityHold = security.Quantity;
var priceHold = Securities[symbolHold].Price;
foreach (var customIndicator in sortedEquityListBySlope.Where(x => x.Key == symbolHold))
{
var numberStocks = (int)Math.Floor(risk / customIndicator.Value.Atr);
if (Math.Abs(quantityHold - numberStocks) > 0 && quantityHold > 1)
{
// Sell or Buy the stocks diff
if (quantityHold > numberStocks)
{
Sell(symbolHold, (quantityHold - numberStocks));
}
else
{
//If the MarketRegimeIndicator indicator is RiskON, we buy stocks, otherwise not;
if (riskON)
{
if (quantityHold < numberStocks)
{
// free cash invested in bonds
if (Portfolio.ContainsKey("IEF"))
if (Portfolio["IEF"].Quantity > 0)
Liquidate("IEF");
decimal portfolioCashBalance = Portfolio.TotalPortfolioValue - Portfolio.TotalHoldingsValue;
// Do we have cash to trade?
if (portfolioCashBalance > ((numberStocks - quantityHold) * priceHold + (numberStocks - quantityHold) * priceHold * BrokerFee))
{
Order(symbolHold, (numberStocks - quantityHold));
}
}
}
}
}
}
}
}
else { _rebalanceWeek = true; }
//If the MarketRegimeIndicator indicator is RiskON, we buy stocks, otherwise not;
if (riskON)
{
foreach (var customIndicatorItem in sortedEquityListBySlope)
{
CustomMomentumIndicator customIndicator = customIndicatorItem.Value;
var symbol = customIndicator.Symbol;
var inPortfolio = false;
foreach (var security in Portfolio.Values.Where(x => x.Invested))
{
if (security.Symbol == symbol)
{
inPortfolio = true;
}
}
if (!inPortfolio)
{
var risk = Portfolio.TotalPortfolioValue * RiskPerContractOnPortfolio;
var numberStocks = (int)Math.Floor(risk / customIndicator.Atr);
var price = Securities[symbol].Price;
if (numberStocks > 0)
{
// free cash invested in bonds
if (Portfolio.ContainsKey("IEF"))
if (Portfolio["IEF"].Quantity > 0)
Liquidate("IEF");
decimal portfolioCashBalance = Portfolio.TotalPortfolioValue - Portfolio.TotalHoldingsValue;
// Do we have cash to trade?
if (portfolioCashBalance > (numberStocks * price + (numberStocks * price) * BrokerFee))
{
Order(symbol, numberStocks);
}
}
}
}
}
// Buy Bonds with remaing cash
if (_marketRegimeFilter.TradeBonds()){
if (Securities["IEF"].Price > 0){
int numberStocks = (int) Math.Floor((Portfolio.TotalPortfolioValue - Portfolio.TotalHoldingsValue - (Portfolio.TotalPortfolioValue - Portfolio.TotalHoldingsValue)*BrokerFee) / Securities["IEF"].Price);
if (numberStocks > 0) {
Order("IEF", numberStocks);
}
}
}
else {
// we free cash invested in bonds
if (Portfolio.ContainsKey("IEF")){
if (Portfolio["IEF"].Quantity > 0) {
Liquidate("IEF");
}
}
}
}
// creating custom indicators for each symbol
public override void OnSecuritiesChanged(SecurityChanges changes)
{
if (changes.AddedSecurities.Count > 0)
{
foreach (Security security in changes.AddedSecurities)
{
if (!_customIndicators.ContainsKey(security.Symbol) && ((security.Symbol.Value != "SPY") || (security.Symbol.Value != "IEF") || (security.Symbol.Value != "BIL")))
{
var customIndicator = new CustomMomentumIndicator(security.Symbol, _annualizedSlopeWindow, _movingAverageWindow, _gapWindow, _atrWindow);
//warm up indicator
var history = History(security.Symbol, customIndicator.Window, Resolution.Daily);
foreach (TradeBar tradeBar in history)
customIndicator.Update(tradeBar);
_customIndicators.Add(security.Symbol, customIndicator);
RegisterIndicator(security.Symbol, customIndicator, Resolution.Daily);
}
}
}
if (changes.RemovedSecurities.Count > 0)
{
foreach (var security in changes.RemovedSecurities)
{
if (security.Invested)
{
Liquidate(security.Symbol);
}
if (_customIndicators.ContainsKey(security.Symbol))
{
_customIndicators.Remove(security.Symbol);
}
}
}
}
}
}