| Overall Statistics |
|
Total Trades 3067 Average Win 0.56% Average Loss -0.36% Compounding Annual Return 18.821% Drawdown 35.300% Expectancy 0.470 Net Profit 1256.789% Sharpe Ratio 1.014 Probabilistic Sharpe Ratio 52.411% Loss Rate 43% Win Rate 57% Profit-Loss Ratio 1.58 Alpha 0.157 Beta -0.042 Annual Standard Deviation 0.151 Annual Variance 0.023 Information Ratio 0.302 Tracking Error 0.228 Treynor Ratio -3.616 Total Fees $19149.02 |
/*
* 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.
*/
using QuantConnect.Data;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Indicators;
using QuantConnect.Data.Custom;
using QuantConnect.Orders;
using QuantConnect.Securities;
using QuantConnect.Data.Consolidators;
namespace QuantConnect.Algorithm.CSharp
{
public class StockRankAlgorithm : QCAlgorithm
{
// algorythm settings
const Resolution _algo_resolution = Resolution.Daily;
private ExponentialMovingAverage _spyMovingAverage;
private const string Spy = "SPY";
private int _trendfilter = 200;
public const decimal _startingcash = 100000m;
public const decimal _maxLeverage = 1.0m;
public const decimal _maxNumHoldings = 25m;
public const decimal _positionweight = _maxLeverage/_maxNumHoldings;
private bool flag1 = true;
private bool flag2 = false;
private int flag3 = 0;
private int _securitiesnum = 0;
private SecurityChanges _changes = SecurityChanges.None;
public DateTime _startdate = new DateTime(2005, 1, 1);
// public DateTime _enddate = new DateTime(2014, 11, 23);
//public DateTime _startdate = new DateTime(2015, 1, 1);
//public DateTime _startdate = DateTime.Now.Date.AddDays(-120);
public DateTime _enddate = DateTime.Now.Date.AddDays(-1);
//public DateTime _enddate = new DateTime(2011, 1, 1);
// sort the data by daily dollar volume and take the top 'NumberOfSymbolsCoarse'
public IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse)
{
if (flag1) {
var hasmorningstar = from x in coarse
where (x.HasFundamentalData)
orderby x.DollarVolume descending
select x.Symbol;
// we need to return only the symbol objects
return hasmorningstar.Take(1000);
}
else {
return Enumerable.Empty<Symbol>();
}
}
// sort the data by P/E ratio and take the top 'NumberOfSymbolsFine'
public IEnumerable<Symbol> FineSelectionFunction(IEnumerable<FineFundamental> fine)
{
if (flag1) {
flag1 = false;
flag2 = true;
ConcurrentDictionary<Symbol, IndicatorBase<IndicatorDataPoint>> averages = new ConcurrentDictionary<Symbol, IndicatorBase<IndicatorDataPoint>>();
// find piatroski score
var piatroskiscore = from x in fine
where x.SecurityReference.SecurityType == "ST00000001" && x.SecurityReference.IsPrimaryShare
where !x.SecurityReference.IsDepositaryReceipt
where x.AssetClassification.MorningstarIndustryGroupCode != MorningstarSectorCode.FinancialServices
where x.AssetClassification.MorningstarIndustryGroupCode != MorningstarSectorCode.Utilities
// where (x.EarningReports.BasicAverageShares.ThreeMonths * (x.EarningReports.BasicEPS.TwelveMonths*x.ValuationRatios.PERatio) > 300000000) //&& x.EarningReports.BasicAverageShares.ThreeMonths * (x.EarningReports.BasicEPS.TwelveMonths*x.ValuationRatios.PERatio) < 2000000000)
let fs = FScore(
x.FinancialStatements.IncomeStatement.NetIncome.TwelveMonths,
x.FinancialStatements.CashFlowStatement.CashFlowFromContinuingOperatingActivities.TwelveMonths,
x.OperationRatios.ROA.ThreeMonths, x.OperationRatios.ROA.OneYear,
x.FinancialStatements.BalanceSheet.ShareIssued.ThreeMonths, x.FinancialStatements.BalanceSheet.ShareIssued.TwelveMonths,
x.OperationRatios.GrossMargin.ThreeMonths, x.OperationRatios.GrossMargin.OneYear,
x.OperationRatios.LongTermDebtEquityRatio.ThreeMonths, x.OperationRatios.LongTermDebtEquityRatio.OneYear, // fix need last year
x.OperationRatios.CurrentRatio.ThreeMonths, x.OperationRatios.CurrentRatio.OneYear, // fix need last year
x.OperationRatios.AssetsTurnover.ThreeMonths, x.OperationRatios.AssetsTurnover.OneYear // fix need last year
)
where (fs >= 6)
//orderby fs descending
let avg = averages.GetOrAdd(x.Symbol, sym => WarmUpIndicator(x.Symbol, new MomentumPercent(90), Resolution.Daily))
// Update returns true when the indicators are ready, so don't accept until they are
where avg.Update(x.EndTime, x.Price)
// only pick symbols who have positive slopes [Absolute Momentum]
where avg > 0m
// prefer symbols with a larger slope
orderby (avg) descending
select x;
// take the top entries from our sorted collection
var topFine = piatroskiscore.Take((int)_maxNumHoldings);
_securitiesnum = topFine.Count();
flag3++;
//Debug("Found " + topFine.Count() + " possibilities");
// foreach (var s in topFine)
// {
// Debug(s.ToString());
// }
// we need to return only the symbol objects
return topFine.Select(x => x.Symbol);
} else {
return Enumerable.Empty<Symbol>();
}
}
// Initialize the data and resolution you require for your strategy:
public override void Initialize()
{
SetStartDate(_startdate);
SetEndDate(_enddate);
// don't do any margin stuff
SetBrokerageModel(Brokerages.BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);
var spy = AddEquity(Spy);
AddEquity("TLT");
SetBenchmark(spy.Symbol);
_spyMovingAverage = EMA(spy.Symbol, _trendfilter, Resolution.Daily);
//Warm up indicator
//IEnumerable<TradeBar> history = History(spy.Symbol, _trendfilter, Resolution.Daily);
//foreach (TradeBar tradeBar in history)
//{
// _spyMovingAverage.Update(tradeBar.EndTime, tradeBar.Close);
//}
//set warm up algorithm to avoid premature trading
SetWarmUp(_trendfilter);
EnableAutomaticIndicatorWarmUp = true;
//Cash allocation
SetCash(_startingcash);
UniverseSettings.Resolution = _algo_resolution;
// this add universe method accepts two parameters:
// - coarse selection function: accepts an IEnumerable<CoarseFundamental> and returns an IEnumerable<Symbol>
// - fine selection function: accepts an IEnumerable<FineFundamental> and returns an IEnumerable<Symbol>
AddUniverse(CoarseSelectionFunction, FineSelectionFunction);
Schedule.On(DateRules.MonthStart(spy.Symbol) , TimeRules.AfterMarketOpen(spy.Symbol), Rebalancing);
}
public override void OnData(Slice slice)
{
if (flag3 > 0)
{
if (flag2) {
flag2 = false;
//
//# if we have no changes, do nothing
if (_changes == SecurityChanges.None) return;
//# liquidate removed securities
foreach (Security security in _changes.RemovedSecurities)
{
if (security.Invested)
Liquidate(security.Symbol);
Debug("Liquidated Stock: " + security.Symbol.Value);
}
decimal diff = _maxNumHoldings - _securitiesnum;
SetHoldings("TLT", _positionweight*diff);
if (Securities[Spy].Price > _spyMovingAverage)
{
foreach (Security security in _changes.AddedSecurities)
{
if (security.Symbol.Value.Equals("SPY")) continue;
if (security.Symbol.Value.Equals("TLT")) continue;
SetHoldings(security.Symbol, _positionweight);
Debug("Purchased Stock: " + security.Symbol.Value);
}
}
_changes = SecurityChanges.None;
if (Portfolio.TotalPortfolioValue > 0)
{
decimal accountLeverage = Portfolio.TotalAbsoluteHoldingsCost / Portfolio.TotalPortfolioValue;
decimal bondLeverage = (Portfolio["TLT"].Quantity*Portfolio["TLT"].Price) / Portfolio.TotalPortfolioValue;
Plot("Leverage", "Leverage", accountLeverage);
Plot("Leverage", "bondLeverage", bondLeverage);
Plot("Memory", "memusage", (float)GC.GetTotalMemory(false));
}
}
}
}
//# this event fires whenever we have changes to our universe
public override void OnSecuritiesChanged(SecurityChanges changes)
{
_changes = changes;
if (changes.AddedSecurities.Count > 0)
{
Debug("Securities added: " + string.Join(",", changes.AddedSecurities.Select(x => x.Symbol.Value)));
}
if (changes.RemovedSecurities.Count > 0)
{
Debug("Securities removed: " + string.Join(",", changes.RemovedSecurities.Select(x => x.Symbol.Value)));
}
}
public void Rebalancing()
{
//can rebalance every month
if (Time.Month % 1 == 0)
//can rebalance once a year in a particular month
//if (Time.Month == 5)
//can rebalance every quarter
//if (Time.Month % 3 == 0)
//can rebalance every two months
//if (Time.Month % 2 == 0)
{
flag1 = true;
}
}
// calculate the f-score
private int FScore(
decimal netincome,
decimal operating_cashflow,
decimal roa_current, decimal roa_past,
decimal issued_current, decimal issued_past,
decimal grossm_current, decimal grossm_past,
decimal longterm_current, decimal longterm_past,
decimal curratio_current, decimal curratio_past,
decimal assetturn_current, decimal assetturn_past
)
{
int fscore = 0;
fscore += (roa_current > 0m ? 1 : 0 ); // return on assets is positive
fscore += ( operating_cashflow > 0m ? 1 : 0 ); // operating cashflow in current year is positive
fscore += ( roa_current >= roa_past ? 1 : 0 ); // roa has increased since last Time
fscore += ( operating_cashflow > roa_current ? 1 : 0 ); // cashflow from current operations are greater than roa
fscore += ( longterm_current <= longterm_past ? 1 : 0 ); // a decrease in the long term debt ratio
fscore += ( curratio_current >= curratio_past ? 1 : 0 ); // an increase in the current ratio
fscore += ( issued_current <= issued_past ? 1 : 0 ); // no new shares have been issued
fscore += ( grossm_current >= grossm_past ? 1 : 0 ); // an increase in gross margin
fscore += ( assetturn_current >= assetturn_past ? 1 : 0 ); // a higher asset turnover ratio
// Debug("<<<<<<");
//Debug("f-score :" +fscore);
// Debug("roa current : " + roa_current);
// Debug("operating cashflow : " + operating_cashflow);
// Debug("roa current : " + roa_current + " past : " + roa_past);
// Debug("operating cashflow : " + operating_cashflow );
// Debug("longterm_current : " + longterm_current + " past : " + longterm_past);
// Debug("curratio_current : " + curratio_current + " past : " + curratio_past);
// Debug("issued current : " + issued_current + " : past " + issued_past);
// Debug("grossm current : " + grossm_current + " : past " + grossm_past);
// Debug("assetturn_current : " + assetturn_current + " : " + assetturn_past);
return fscore;
}
}
}