| Overall Statistics |
|
Total Trades 15 Average Win 80.25% Average Loss -7.30% Compounding Annual Return 149.942% Drawdown 82.400% Expectancy 1.998 Net Profit 149.942% Sharpe Ratio 4.454 Probabilistic Sharpe Ratio 64.335% Loss Rate 75% Win Rate 25% Profit-Loss Ratio 10.99 Alpha 4.29 Beta 10.8 Annual Standard Deviation 1.569 Annual Variance 2.461 Information Ratio 4.539 Tracking Error 1.484 Treynor Ratio 0.647 Total Fees $333.00 |
using QuantConnect.Securities.Future;
using QuantConnect.Data.Market;
namespace QuantConnect.Algorithm.CSharp
{
public class BuyAndHoldFuturesTemplate : QCAlgorithm
{
private Future _future;
private FuturesContract _activeContract;
private bool _isInitializedForFutures = false;
private int _DaysBeforeExpiryToRollover = 1;
public override void Initialize()
{
SetStartDate(2019, 1, 1);
SetEndDate(2020, 1, 1);
SetCash(100000);
_future = AddFuture(Futures.Indices.SP500EMini, Resolution.Minute);
_future.SetFilter(0, 600);
// optional, helps reduce margin calls
Settings.FreePortfolioValuePercentage = 0.3m;
}
override public void OnData(Slice slice)
{
// for QC API reasons, futures are best initialized in the OnData method
if (!_isInitializedForFutures)
{
InitializeForFutures(slice);
return;
}
// indicator (alpha) generating logic goes here
// ...
}
private void InitializeForFutures(Slice slice)
{
SetActiveContractAndScheduleRollover(slice);
SetHoldings(_activeContract.Symbol, 1);
Log($"New contract purchased: {_activeContract.Symbol.Value}");
_isInitializedForFutures = true;
}
#region FUTURES ROLLOVER OVERHEAD
private void SetActiveContractAndScheduleRollover(Slice slice)
{
if (slice.FutureChains.Count != 1)
{
Log($"ERROR - No contracts in var {nameof(slice.FutureChains)}");
return;
}
var esMiniChain = slice.FutureChains.First();
var recentContracts = esMiniChain.Value
.Where(x => (x.Expiry - Time.Date).TotalDays > _DaysBeforeExpiryToRollover)
.OrderBy(x => (x.Expiry - Time.Date).TotalDays)
.ToList();
if (recentContracts.Count == 0)
{
_activeContract = null;
Log($"ERROR - No contracts in var {nameof(recentContracts)}, no active contract assigned, no scheduled rollover. Liquidating portfolio.");
Liquidate();
Log($"Portfolio liquidated.");
return;
}
Log($"list of sorted recent contracts: {string.Join(", ", recentContracts.Select(x => x.Symbol.Value).ToList())}");
var frontContract = recentContracts.First();
_activeContract = frontContract;
Log($"New active contract set to {_activeContract.Symbol.Value}");
ScheduleRollover(slice);
}
private void ScheduleRollover(Slice slice)
{
// hack to pass Slice to Rollover callback, see https://social.msdn.microsoft.com/Forums/en-US/fd7dcd64-5ec3-4951-81ab-2ca4f7dc06b2/
Action rolloverCallback = () => { Rollover(slice); };
Log($"Contract {_activeContract.Symbol.Value} has rollover scheduled for: {_activeContract.Expiry.AddDays(-1 * _DaysBeforeExpiryToRollover).ToShortDateString()}");
Schedule.On(DateRules.On(_activeContract.Expiry.AddDays(-1 * _DaysBeforeExpiryToRollover)),
TimeRules.AfterMarketOpen(_activeContract.Symbol),
rolloverCallback);
}
private void Rollover(Slice slice)
{
SetActiveContractAndScheduleRollover(slice);
if (Portfolio.Invested)
{
Liquidate();
Log($"Portfolio liquidated.");
SetHoldings(_activeContract.Symbol, 1);
Log($"New contract purchased: {_activeContract.Symbol.Value}");
}
}
#endregion
}
}