| Overall Statistics |
|
Total Trades 750 Average Win 3.02% Average Loss -2.75% Compounding Annual Return 13.877% Drawdown 44.200% Expectancy 0.140 Net Profit 202.106% Sharpe Ratio 0.463 Loss Rate 46% Win Rate 54% Profit-Loss Ratio 1.10 Alpha 0.126 Beta 0.034 Annual Standard Deviation 0.276 Annual Variance 0.076 Information Ratio 0.23 Tracking Error 0.326 Treynor Ratio 3.753 Total Fees $1500.00 |
namespace QuantConnect
{
public class Mundo : QCAlgorithm
{
List<string> _symbols = new List<string>() { "EURUSD", "GBPUSD", "AUDUSD", "NZDUSD", "USDCAD", "USDCHF" };
TradeBars _bars = new TradeBars();
private Dictionary<string, SymbolData> _symbolData = new Dictionary<string, SymbolData>();
public readonly int RollingWindowSize = 2;
public readonly TimeSpan BarPeriod = TimeSpan.FromDays(1);
//parameters
int _normPeriod = 40;
int _period = 20;
//Initialize the data and resolution you require for your strategy:
public override void Initialize()
{
//Start and End Date range for the backtest:
SetStartDate(2007, 4, 1);
SetEndDate(2015, 10, 1);
//SetEndDate(DateTime.Now.Date.AddDays(-1));
//Cash allocation
SetCash(10000);
// initialize data
foreach (var symbol in _symbols)
{
_symbolData.Add(symbol, new SymbolData(symbol, SecurityType.Forex, BarPeriod, RollingWindowSize));
}
foreach(var kvp in _symbolData)
{
var symbolData = kvp.Value;
AddSecurity(symbolData.SecurityType, symbolData.Symbol, Resolution.Hour);
// define a consolidator to consolidate data for this symbol on the requested period
var consolidator = new TradeBarConsolidator(BarPeriod);
// define indicators
symbolData._sd = new StandardDeviation(_period);
symbolData._min = new Minimum(_normPeriod);
symbolData._max = new Maximum(_normPeriod);
//update indicators
consolidator.DataConsolidated += (sender, bar) =>
{
// 'bar' here is our newly consolidated data
symbolData._min.Update(bar.Time, symbolData._portfolio);
symbolData._max.Update(bar.Time, symbolData._portfolio);
symbolData._sd.Update(bar.Time, bar.Close);
// we're also going to add this bar to our rolling window so we have access to it later
symbolData.Bars.Add(bar);
};
// we need to add this consolidator so it gets auto updates
SubscriptionManager.AddConsolidator(symbolData.Symbol, consolidator);
}
}
//Data Event Handler: New data arrives here. "TradeBars" type is a dictionary of strings so you can access it by symbol.
public void OnData(TradeBars data)
{
UpdateBars(data);
if (_bars.Count != _symbols.Count) return;
decimal _totalSd = 0;
decimal _beta = 0;
decimal _prt = 0;
foreach (var _data in _symbolData.Values)
{
if(!_data._sd.IsReady) return;
if(_data._sd != 0)
{
_totalSd += _data._sd;
_beta += _data._sd;
}
}
foreach (var _data in _symbolData.Values)
{
//make portfolio with Mundo index beta weights
if(_beta != 0) _prt += _bars[_data.Symbol].Close*_data._sd/(_beta/6m);
}
Plot("Cash", "prtSMAF", Portfolio.Cash);
Plot("Portfolio curve", "prt", _prt);
foreach (var _data in _symbolData.Values)
{
_data._portfolio = _prt;
if((_data._max - _data._min) != 0) _data._norm = (_prt - _data._min)/(_data._max - _data._min);
//Plot("Normalized curve", "n", _data._norm);
//------------------------------------------------------------------------------------------ EXITS
if(Portfolio[_data.Symbol].IsLong && _data._norm > 1m)
{
Liquidate();
}
if(Portfolio[_data.Symbol].IsShort && _data._norm < 0m)
{
Liquidate();
}
//------------------------------------------------------------------------------------------ ENTRIES
if(!Portfolio[_data.Symbol].Invested && _data._norm > 0 && _data._prenorm < 0
//&& (_bars[_data.Symbol].Time.Date.DayOfWeek == DayOfWeek.Monday
//|| _bars[_data.Symbol].Time.Date.DayOfWeek == DayOfWeek.Tuesday
)//)
{
if((_beta/4m) != 0) SetHoldings(_data.Symbol, 0.6m/(_data._sd/(_beta/4m)));
//Log("Opened");
}
if(!Portfolio[_data.Symbol].Invested && _data._norm < 1m && _data._prenorm > 1m
//&& (_bars[_data.Symbol].Time.Date.DayOfWeek == DayOfWeek.Monday
//|| _bars[_data.Symbol].Time.Date.DayOfWeek == DayOfWeek.Tuesday
)//)
{
if((_beta/4m) != 0) SetHoldings(_data.Symbol, -0.6m/(_data._sd/(_beta/4m)));
}
//some prev value
_data._prenorm = _data._norm;
}
} // end ondata
//Update the global "_bars" object
private void UpdateBars(TradeBars data)
{
foreach (var bar in data.Values)
{
if (!_bars.ContainsKey(bar.Symbol))
{
_bars.Add(bar.Symbol, bar);
}
_bars[bar.Symbol] = bar;
}
}
class SymbolData
{
//stuff
public readonly string Symbol;
public readonly SecurityType SecurityType;
public readonly RollingWindow<TradeBar> Bars;
public readonly TimeSpan BarPeriod;
//indcators
public StandardDeviation _sd;
public Minimum _min;
public Maximum _max;
public decimal _portfolio;
public decimal _norm;
public decimal _prenorm;
public SymbolData(string symbol, SecurityType securityType, TimeSpan barPeriod, int windowSize) //, QCAlgorithm algorithm
{
Symbol = symbol;
SecurityType = securityType;
BarPeriod = barPeriod;
Bars = new RollingWindow<TradeBar>(windowSize);
_portfolio = new decimal();
_norm = new decimal();
_prenorm = new decimal();
}
public bool IsReady
{
get {
return Bars.IsReady
&& _sd.IsReady
&& _min.IsReady
&& _max.IsReady
;}
}
public bool WasJustUpdated(DateTime current)
{
return Bars.Count > 0 && Bars[0].Time == current - BarPeriod;
}
}
} // close algo
}