| Overall Statistics |
|
Total Orders 0 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Start Equity 100000 End Equity 100000 Net Profit 0% Sharpe Ratio 0 Sortino Ratio 0 Probabilistic Sharpe Ratio 0% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio -1.804 Tracking Error 0.13 Treynor Ratio 0 Total Fees $0.00 Estimated Strategy Capacity $0 Lowest Capacity Asset Portfolio Turnover 0% Drawdown Recovery 0 |
#region imports
using System;
using System.Linq;
using QuantConnect;
using QuantConnect.Util;
using QuantConnect.Algorithm;
using QuantConnect.Securities;
using QuantConnect.Data.Market;
using System.Collections.Generic;
using QuantConnect.Data.UniverseSelection;
#endregion
public class LiquidNonPennyStocksUniverseAlgorithm : QCAlgorithm
{
public override void Initialize()
{
SetStartDate(2021, 1, 1);
SetEndDate(2021, 4, 1);
AddUniverse(fundamental =>
{
var stocks = fundamental
.Where(c => c.HasFundamentalData && c.AdjustedPrice > 5 && c.AdjustedPrice < 50)
.Select(a => a.Symbol).ToHashSet();
Log($"Coarse filter located {stocks.Count()} stocks");
var historyBySymbol = stocks.ToDictionary(k=>k, v=> new List<TradeBar>());
foreach(var bars in History<TradeBar>(stocks, 20, Resolution.Daily))
{
foreach(var (symbol, bar) in bars)
{
historyBySymbol[symbol].Add(bar);
}
}
var rsiFiltered = historyBySymbol.Where(a =>
{
if (a.Value == null || a.Value.Count < 20)
return false;
var rsi = CalculateRsi(a.Value.Select(a => a.Close).ToList(), 3);
return rsi <= 10;
}).Select(a => a.Key)
.ToList();
Log($"RSI filter located {rsiFiltered.Count()} stocks");
return rsiFiltered;
});
}
public override void OnSecuritiesChanged(SecurityChanges changes)
{
Log(changes.ToString());
}
public static decimal CalculateRsi(List<decimal> prices, int period = 5)
{
if (prices == null || prices.Count < period + 1)
{
throw new ArgumentException("Not enough price data to compute RSI");
}
// 1) Compute price deltas
var deltas = new decimal[prices.Count - 1];
for (int i = 1; i < prices.Count; i++)
{
deltas[i - 1] = prices[i] - prices[i - 1];
}
// 2) Separate gains and losses
var gain = new decimal[deltas.Length];
var loss = new decimal[deltas.Length];
for (int i = 0; i < deltas.Length; i++)
{
gain[i] = deltas[i] > 0 ? deltas[i] : 0m;
loss[i] = deltas[i] < 0 ? -deltas[i] : 0m;
}
// 3) First average gain/loss
decimal avgGain = 0m, avgLoss = 0m;
for (int i = 0; i < period; i++)
{
avgGain += gain[i];
avgLoss += loss[i];
}
avgGain /= period;
avgLoss /= period;
// 4) Wilder's smoothing for subsequent points
for (int i = period; i < gain.Length; i++)
{
avgGain = ((period - 1) * avgGain + gain[i]) / period;
avgLoss = ((period - 1) * avgLoss + loss[i]) / period;
}
// 5) Compute final RSI
if (Math.Abs(avgLoss) < 1e-14M)
{
return 100m;
}
decimal rs = avgGain / avgLoss;
decimal rsi = 100m - (100m / (1m + rs));
return rsi;
}
}