| Overall Statistics |
|
Total Trades 0 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Net Profit 0% Sharpe 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 0 Tracking Error 0 Treynor Ratio 0 Total Fees $0.00 |
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Algorithm.Framework.Selection;
using QuantConnect.Brokerages;
using QuantConnect.Data;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Indicators;
using QuantConnect.Orders;
namespace QuantConnect.Algorithm.CSharp
{
public class TopGainersWIthIndicatorsAlgorithm : QCAlgorithm
{
private readonly Dictionary<DateTime, List<string>> _backtestSymbolsPerDay = new Dictionary<DateTime, List<string>>();
private const string fileUrl = @"https://www.dropbox.com/s/rc7xay8voo7elol/stock-picker-backtest-2020-10-16.csv?dl=1";
private Dictionary<string, SecurityInfo> _securityDetails;
private decimal _stopLossPercent = 0.98m; //2% trailing loss
private decimal _maxProfit = 1.011m; //1.10% Profit
decimal minimumPurchase = 500m;
private int _momentumPeriod = 60;
private int _priceIncreaseFrequency = 3;
private int _priceIncreaseFrequencyOverrideMomentumPeriod = 12;
private int _startHour = 9;
private int _startMin = 30;
private bool _tradeDataAvailable = false;
private decimal _percentBelowDailyHigh = 0.02m;
public override void Initialize()
{
UniverseSettings.Resolution = Resolution.Second;
SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);
SetStartDate(2020, 10, 23);
SetEndDate(2020, 10, 23);
SetCash(100000);
SetTimeZone("America/New_York");
//Transactions.MarketOrderFillTimeout = TimeSpan.FromSeconds(30);
_securityDetails = new Dictionary<string, SecurityInfo>();
SetUniverseSelection(
new ScheduledUniverseSelectionModel(
DateRules.EveryDay(),
TimeRules.Every(TimeSpan.FromMinutes(200)),
SelectSymbols
)
);
SetWarmUp(_momentumPeriod, Resolution.Second);
Schedule.On(
DateRules.EveryDay(),
// 15 minutes before market close,
TimeRules.At(15, 45),
() =>
{
// before market close we Liquidate ALL
Liquidate();
}
);
Schedule.On(
DateRules.EveryDay(),
TimeRules.At(18, 45),
() =>
{
_securityDetails = new Dictionary<string, SecurityInfo>();
_backtestSymbolsPerDay.Clear();
UniverseManager.Clear();
}
);
}
public override void OnSecuritiesChanged(SecurityChanges changes)
{
Debug($"OnSecuritiesChanged {Time}: {changes}");
Debug($" added {string.Join(",", changes.AddedSecurities.Select(x => x.Symbol.Value))}");
Debug($" removed {string.Join(",", changes.RemovedSecurities.Select(x => x.Symbol.Value))}");
}
private IEnumerable<Symbol> SelectSymbols(DateTime dateTime)
{
var file = Download(fileUrl);
_backtestSymbolsPerDay.Clear();
UniverseManager.Clear();
foreach (var line in file.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries))
{
var csv = line.ToCsv();
var date = DateTime.ParseExact(csv[0], "yyyyMMdd", null);
var symbols = csv.Skip(1).ToList();
if (_backtestSymbolsPerDay.ContainsKey(date))
{
_backtestSymbolsPerDay[date].AddRange(symbols);
}
else
{
_backtestSymbolsPerDay[date] = symbols;
}
}
List<string> result;
if (_backtestSymbolsPerDay.TryGetValue(dateTime.Date, out result))
{
var response = result.Distinct().
Select(x => QuantConnect.Symbol.Create(x, SecurityType.Equity, Market.USA));
Debug($"*******{dateTime} {string.Join(",", response)}");
return response;
}
_tradeDataAvailable = false;
return new List<Symbol>() { };
}
public override void OnData(Slice data)
{
foreach (var kvp in data.Bars)
{
if (!_securityDetails.ContainsKey(kvp.Key.Value))
{
_securityDetails[kvp.Key.Value] = new SecurityInfo()
{
TradeBars = new List<TradeBar>(),
Symbol = kvp.Key,
MomentumPercent = MOMP(kvp.Key, _momentumPeriod, Resolution.Second),
RelativeStrengthIndex = RSI(kvp.Key, _momentumPeriod, resolution: Resolution.Second),
};
}
;
var security = _securityDetails[kvp.Key.Value];
Debug($"{kvp.Key} security.MomentumPercent {security.MomentumPercent} security.RelativeStrengthIndex {security.RelativeStrengthIndex}");
var bar = kvp.Value;
if (bar.High > security.HighestPrice)
{
security.HighestPrice = bar.High;
if (Portfolio[kvp.Key].Invested)
{
UpdateStopTrailingLoss(security, bar.Symbol);
}
}
if (security.WarmupComplete && !Portfolio[kvp.Key].Invested)
{
if ((security.RelativeStrengthIndex > 50
&& security.BollingerBands < 0) ||
(security.MomentumPercent > 1
&& security.MovingAverageConvergenceDivergence > 0)
)
{
SetHoldings(bar.Symbol, 0.8);
}
}
}
}
private void UpdateStopTrailingLoss(SecurityInfo securityDetail, Symbol sym)
{
var orderTicket = securityDetail.StopTrailingLossOrderTicket;
if (orderTicket != null)
{
orderTicket.Update(new UpdateOrderFields()
{
StopPrice = _stopLossPercent * securityDetail.HighestPrice
});
if (_stopLossPercent * securityDetail.HighestPrice >= securityDetail.FillPrice * _maxProfit)
{
if (securityDetail.StoLimit != null && securityDetail.StoLimit.OrderId > 0)
{
securityDetail.StoLimit.Cancel();
}
}
Plot("Data Chart", "Stop Price", orderTicket.Get(OrderField.StopPrice));
}
else if (_securityDetails[sym.Value].StoLimit?
.OrderId > 0)
{
_securityDetails[sym.Value].StopTrailingLossOrderTicket
= StopMarketOrder(sym,
-Portfolio[sym].Quantity,
_securityDetails[sym.Value].FillPrice * _stopLossPercent);
}
}
public override void OnOrderEvent(OrderEvent orderEvent)
{
if (orderEvent.Status == OrderStatus.Filled && orderEvent.Direction == OrderDirection.Buy)
{
_securityDetails[orderEvent.Symbol.Value].FillPrice = orderEvent.FillPrice;
_securityDetails[orderEvent.Symbol.Value].HoldingQuantity = orderEvent.Quantity;
//Only Take Profit if Stop trailing loss is Lower than profit
_securityDetails[orderEvent.Symbol.Value].StoLimit = LimitOrder(
orderEvent.Symbol,
-Portfolio[orderEvent.Symbol].Quantity,
_securityDetails[orderEvent.Symbol.Value].FillPrice * _maxProfit);
Debug($"{orderEvent.Symbol} filled@ { orderEvent.FillPrice} " +
$"qty {orderEvent.Quantity} " +
$"sell@ {_securityDetails[orderEvent.Symbol.Value].FillPrice * _maxProfit} " +
$"stopLoss@ {_securityDetails[orderEvent.Symbol.Value].FillPrice * _stopLossPercent}");
}
else if (orderEvent.Status == OrderStatus.Filled && orderEvent.Direction == OrderDirection.Sell
)
{
var order = Transactions.GetOrderById(orderEvent.OrderId);
if (order.Type == OrderType.Limit || order.Type == OrderType.StopMarket)
{
Transactions.CancelOpenOrders(order.Symbol);
}
}
}
}
public class SecurityInfo
{
public Symbol Symbol { get; set; }
public decimal HighestPriceForToday { get; set; }
public decimal HighestPrice { get; set; }
public decimal FillPrice { get; set; }
public MomentumPercent MomentumPercent { get; set; }
public RelativeStrengthIndex RelativeStrengthIndex { get; set; }
public BollingerBands BollingerBands { get; set; }
public MovingAverageConvergenceDivergence MovingAverageConvergenceDivergence { get; set; }
public bool WarmupComplete =>
this.MomentumPercent.IsReady && this.RelativeStrengthIndex.IsReady;
public List<TradeBar> TradeBars { get; set; }
public decimal HoldingQuantity { get; set; }
public OrderTicket StopTrailingLossOrderTicket { get; set; }
public OrderTicket StoLimit { get; set; }
public void UpdateIndicators(DateTime time, decimal price)
{
this.MomentumPercent.Update(time, price);
this.RelativeStrengthIndex.Update(time, price);
}
}
}