| Overall Statistics |
|
Total Trades 1454 Average Win 0.31% Average Loss -0.46% Compounding Annual Return -8.594% Drawdown 70.000% Expectancy -0.195 Net Profit -60.227% Sharpe Ratio -0.167 Loss Rate 52% Win Rate 48% Profit-Loss Ratio 0.67 Alpha -0.047 Beta 0.181 Annual Standard Deviation 0.224 Annual Variance 0.05 Information Ratio -0.336 Tracking Error 0.262 Treynor Ratio -0.208 Total Fees $3078.37 |
namespace QuantConnect
{
/// <summary>
/// Every month, the investor considers whether the excess return of each asset over the past 12
/// months is positive or negative and goes long on the contract if it is positive and short if
/// negative. The position size is set to be inversely proportional to the instrument’s volatility.
/// </summary>
/// <seealso cref="QuantConnect.Algorithm.QCAlgorithm" />
public class TimeSeriesMomentumEffect : QCAlgorithm
{
private readonly Dictionary<Symbol, decimal> excessReturns = new Dictionary<Symbol, decimal>();
private readonly string[] forexTickers =
{
"EURUSD", "USDJPY", "USDCHF", "GBPUSD", "USDCAD", "AUDUSD"
};
// The full list of Futures can be found in Table 1
// at http://pages.stern.nyu.edu/~lpederse/papers/TimeSeriesMomentum.pdf
// Here I'll use only commodities already listed in the Futures Class.
private readonly string[] futuresTickers =
{
// Commodities available data.
Futures.Metals.Gold,
Futures.Metals.Platinum,
Futures.Metals.Silver,
Futures.Energies.CrudeOilWTI,
Futures.Energies.HeatingOil,
Futures.Energies.Gasoline,
Futures.Energies.NaturalGas,
Futures.Grains.Corn,
Futures.Grains.Soybeans,
Futures.Grains.SoybeanMeal,
Futures.Grains.SoybeanOil,
Futures.Grains.Wheat,
Futures.Indices.SP500EMini,
Futures.Meats.LeanHogs,
Futures.Meats.FeederCattle,
Futures.Softs.Cocoa,
Futures.Softs.Coffee,
Futures.Softs.Cotton2,
Futures.Softs.Sugar11
};
// The Generally the 10 year maturity is used as a proxy of the risk free rate.
private readonly string riskFreeReturnQuandlCode = "USTREASURY/YIELD";
//
private readonly List<Symbol> symbols = new List<Symbol>();
private bool monthlyRebalance;
private decimal riskFreeRetun;
public override void Initialize()
{
// Set the basic algorithm parameters.
SetStartDate(2006, 06, 01);
SetEndDate(2016, 08, 30);
SetCash(100000);
AddData<QuandlUSTeasuryYield>(riskFreeReturnQuandlCode, Resolution.Daily);
//foreach (var ticker in futuresTickers)
// symbols.Add(AddFuture(ticker, Resolution.Daily).Symbol);
foreach (var ticker in forexTickers)
{
var forex = AddForex(ticker, Resolution.Daily, Market.Oanda);
symbols.Add(forex.Symbol);
// https://en.wikipedia.org/wiki/Coefficient_of_variation
forex.VolatilityModel =
new IndicatorVolatilityModel<IndicatorDataPoint>(STD(forex.Symbol, 20, Resolution.Daily)
.Over(SMA(forex.Symbol, 20, Resolution.Daily)));
}
Schedule.On(DateRules.MonthStart(), TimeRules.At(0, 0), () =>
{
UpdateAssetsReturns();
monthlyRebalance = true;
});
}
private void UpdateAssetsReturns()
{
var dateRequest = new DateTime(Time.Year - 1, Time.Month, Time.Day);
// I ask for some days before just in case the selected day hasn't historical prices record.
var history = History(symbols, dateRequest.AddDays(-5), dateRequest.AddDays(1), Resolution.Daily);
foreach (var symbol in symbols)
{
Slice slice;
try
{
slice = history.Last(s => s.ContainsKey(symbol));
excessReturns[symbol] = (Securities[symbol].Price / slice[symbol].Price - 1m) * 100m -
riskFreeRetun;
}
catch (Exception e)
{
Console.WriteLine(symbol, " hasn't data to estimate excess returns.");
excessReturns[symbol] = 0m;
}
}
}
public override void OnData(Slice slice)
{
if (monthlyRebalance)
{
var assetsToLong = excessReturns.Where(s => s.Value > 0m)
.ToDictionary(pair => pair.Key,
pair => Securities[pair.Key].VolatilityModel.Volatility);
var assetsToShort = excessReturns.Where(s => s.Value < 0m)
.ToDictionary(pair => pair.Key,
pair => Securities[pair.Key].VolatilityModel.Volatility);
var tradesToClose = Securities.Where(
secPair => Portfolio[secPair.Key].Invested &&
!assetsToShort.Keys.Concat(assetsToLong.Keys).Contains(secPair.Key))
.Select(s => s.Key);
foreach (var symbol in symbols)
{
Liquidate(symbol);
}
var divisor = assetsToLong.Values.Sum();
foreach (var keyValuePair in assetsToLong)
SetHoldings(keyValuePair.Key, 0.2m * keyValuePair.Value / divisor);
divisor = assetsToShort.Values.Sum();
foreach (var keyValuePair in assetsToShort)
SetHoldings(keyValuePair.Key, -0.2m * keyValuePair.Value / divisor);
monthlyRebalance = false;
}
}
public void OnData(Quandl data)
{
riskFreeRetun = data.Price;
}
}
public class QuandlUSTeasuryYield : Quandl
{
public QuandlUSTeasuryYield()
: base("10 YR")
{
}
}
}