| Overall Statistics |
|
Total Trades 157 Average Win 3.88% Average Loss -0.98% Compounding Annual Return 4.423% Drawdown 17.400% Expectancy 0.774 Net Profit 72.687% Sharpe Ratio 0.526 Loss Rate 64% Win Rate 36% Profit-Loss Ratio 3.94 Alpha 0.022 Beta 0.242 Annual Standard Deviation 0.09 Annual Variance 0.008 Information Ratio -0.357 Tracking Error 0.165 Treynor Ratio 0.196 Total Fees $173.73 |
using System.Collections.Concurrent;
namespace QuantConnect
{
public class KAMA : QCAlgorithm
{
string tradeSymbol = "SPY";
//Data Required
List<string> _symbols = new List<string>() { "SPY" };
RollingWindow<TradeBar> _window = new RollingWindow<TradeBar>(110);
private RollingWindow<decimal> _amaW = new RollingWindow<decimal>(5);
TradeBars _bars = new TradeBars();
//global vars
decimal _delta;
decimal _noise;
decimal _er;
decimal _c;
decimal _ama;
decimal _prevama;
decimal _slowsc;
decimal _fastsc;
decimal _s;
StandardDeviation _sd;
//Sum _sum;
//kama parameters
int _period = 100;
int _fast = 2;
int _slow = 30;
int _filterPeriod = 100;
//double _filterIndex = 0.1; // used for sl*ATR
//Initialize the data and resolution you require for your strategy:
public override void Initialize()
{
//Start and End Date range for the backtest:
SetStartDate(2003, 1, 1);
//SetEndDate(2015, 6, 5);
SetEndDate(DateTime.Now.AddDays(-1));
//Cash allocation
SetCash(25000);
//Add as many securities as you like. All the data will be passed into the event handler:
foreach (var symbol in _symbols) {
AddSecurity(SecurityType.Equity, symbol, Resolution.Daily);
Securities[symbol].SetDataNormalizationMode(DataNormalizationMode.Raw);
//Securities[symbol].TransactionModel = new CustomTransactionModel();
}
//Add as many securities as you like. All the data will be passed into the event handler:
/*foreach (var symbol in _symbols) {
AddSecurity(SecurityType.Forex, symbol, Resolution.Daily);
//Securities[symbol].SetLeverage(50m);
//Securities[symbol].TransactionModel = new CustomForexTransactionModel();
}*/
//_symbols.AddRange(_EqSymbols);
//updated inside OnData
//_sum = new Sum(_period);
_sd = new StandardDeviation(_filterPeriod);
}
//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;
//foreach(var symbol in _symbols)
//{
try
{
_window.Add(_bars[tradeSymbol]);
if(!_window.IsReady) return;
_er = 1m;
_delta = Math.Abs(_window[0].Close - _window[_period].Close);
_noise = Math.Abs(_window[0].Close - _window[1].Close);
_s = 0;
for (int i=_period; i >= 0; i--)
{
_s += Math.Abs(_window[i].Close - _window[i+1].Close);
}
if(_s != 0)
{
_er = _delta/_s;
}
_slowsc = 2m /(_slow+1);
_fastsc = 2m /(_fast+1);
_c = (_er*(_fastsc - _slowsc) + _slowsc) * (_er*(_fastsc - _slowsc) + _slowsc);
//original / optimised kama
if(_prevama != 0)
{
_ama = _prevama + _c*(_window[0].Close - _prevama);
} else
{
_ama = _window[0].Close;
}
_amaW.Add(_ama);
if(!_amaW.IsReady) return;
TradeBar bar;
if (_bars.TryGetValue(tradeSymbol, out bar))
{
_sd.Update(bar.Time, (_amaW[0]-_amaW[1]));
}
//indies
Plot("KAMA", "ama window", _ama);
Plot("KAMA StDev", "ama window", _sd);
//exits
if(Portfolio.HoldStock)
{
//10% stop loss of everything vavailable
if (Securities[tradeSymbol].Holdings.UnrealizedProfit < -Portfolio.TotalPortfolioValue*0.1m)
{
Liquidate(tradeSymbol);
}
if(Portfolio[tradeSymbol].IsLong)// && _amaW[0] < _amaW[1] && _amaW[1] > _amaW[2])
{
//decimal max = _amaW[1] - _amaW[0];
if(_amaW[0] < _amaW[1])// && max < _sd*(decimal)_filterIndex)
{
Liquidate(tradeSymbol);
}
}
}
//entries
if(_prevama != 0)
{
if(!Portfolio.HoldStock)// && _amaW[0] > _amaW[1] && _amaW[1] < _amaW[2])
{
//decimal min = _amaW[0] - _amaW[1];
if(_amaW[0] > _amaW[1])// && min > _sd*(decimal)_filterIndex)
{
SetHoldings(tradeSymbol,0.9);
}
}
}
_prevama = _ama;
}
catch(Exception err)
{
Log("ERROR. "+err.Message);
}
} //end of 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;
}
}
} // end of algo
} //end of namesace
namespace QuantConnect.Securities.Forex
{
/// <summary>
/// Forex Transaction Model Class: Specific transaction fill models for FOREX orders
/// </summary>
/// <seealso cref="SecurityTransactionModel"/>
/// <seealso cref="ISecurityTransactionModel"/>
public class CustomForexTransactionModel : SecurityTransactionModel
{
private readonly decimal _commissionRate;
private readonly decimal _minimumOrderFee;
/// <summary>
/// Initialise the transaction model class
/// </summary>
public CustomForexTransactionModel(decimal monthlyTradeAmountInUSDollars = 0)
{
/* Interactive Brokers Forex Commisions as of 2015.04.15
Monthly Trade Amount Commissions Minimum per Order
<=USD 1,000,000,000 0.20basis point * Trade Value USD 2.00
USD 1,000,000,001 - 2,000,000,000 0.15basis point * Trade Value USD 1.50
USD 2,000,000,001 - 5,000,000,000 0.10basis point * Trade Value USD 1.25
>USD 5,000,000,000 0.08basis point * Trade Value USD 1.00
*
*/
const decimal bp = 0.0001m;
if (monthlyTradeAmountInUSDollars <= 1000000000) // 1 billion
{
_commissionRate = 0.20m*bp;
_minimumOrderFee = 2.00m;
}
else if (monthlyTradeAmountInUSDollars <= 2000000000) // 2 billion
{
_commissionRate = 0.15m*bp;
_minimumOrderFee = 1.50m;
}
else if (monthlyTradeAmountInUSDollars <= 5000000000) // 5 billion
{
_commissionRate = 0.20m*bp;
_minimumOrderFee = 1.25m;
}
else
{
_commissionRate = 0.20m*bp;
_minimumOrderFee = 1.00m;
}
}
/// <summary>
/// Get the slippage approximation for this order
/// </summary>
/// <returns>Decimal value of the slippage approximation</returns>
/// <seealso cref="Order"/>
public override decimal GetSlippageApproximation(Security security, Order order)
{
//Return 0 by default
decimal slippage = 0.0002m;
//For FOREX, the slippage is the Bid/Ask Spread for Tick, and an approximation for the
switch (security.Resolution)
{
case Resolution.Minute:
case Resolution.Second:
//Get the last data packet:
//Assume slippage is 1/10,000th of the price
slippage = security.GetLastData().Value*0.0002m;
break;
case Resolution.Tick:
var lastTick = (Tick) security.GetLastData();
switch (order.Direction)
{
case OrderDirection.Buy:
//We're buying, assume slip to Asking Price.
slippage = Math.Abs(order.Price - lastTick.AskPrice);
break;
case OrderDirection.Sell:
//We're selling, assume slip to the bid price.
slippage = Math.Abs(order.Price - lastTick.BidPrice);
break;
}
break;
}
return slippage;
}
/// <summary>
/// Get the fees from this order
/// </summary>
/// <param name="quantity">Quantity of purchase</param>
/// <param name="price">Price of the currency</param>
/// <remarks>
/// FXCM now uses a flat fee per trade instead of a spread model. This spread model is
/// out of date but the data has the spread built into historical data. >> New data source needed.
/// </remarks>
/// <returns>Decimal value of the order fee</returns>
public override decimal GetOrderFee(decimal quantity, decimal price)
{
var fee = _commissionRate*quantity*price;
return Math.Max(_minimumOrderFee, fee);
}
/// <summary>
/// Default implementation returns 0 for fees.
/// </summary>
/// <param name="security">The security matching the order</param>
/// <param name="order">The order to compute fees for</param>
/// <returns>The cost of the order in units of the account currency</returns>
public override decimal GetOrderFee(Security security, Order order)
{
var forex = (Forex) security;
// get the total order value in the account currency
var price = order.Status.IsFill() ? order.Price : security.Price;
var totalOrderValue = order.GetValue(price)*forex.QuoteCurrency.ConversionRate;
var fee = _commissionRate*totalOrderValue;
return Math.Max(_minimumOrderFee, fee);
}
}
}
namespace QuantConnect.Securities.Forex
{
/// <summary>
/// Forex Transaction Model Class: Specific transaction fill models for FOREX orders
/// </summary>
/// <seealso cref="SecurityTransactionModel"/>
/// <seealso cref="ISecurityTransactionModel"/>
public class CustomTransactionModel : SecurityTransactionModel
{
private readonly decimal _commissionRate;
private readonly decimal _minimumOrderFee;
/// <summary>
/// Initialise the transaction model class
/// </summary>
public CustomTransactionModel(decimal monthlyTradeAmountInUSDollars = 0)
{
/* Interactive Brokers Forex Commisions as of 2015.04.15
Monthly Trade Amount Commissions Minimum per Order
<=USD 1,000,000,000 0.20basis point * Trade Value USD 2.00
USD 1,000,000,001 - 2,000,000,000 0.15basis point * Trade Value USD 1.50
USD 2,000,000,001 - 5,000,000,000 0.10basis point * Trade Value USD 1.25
>USD 5,000,000,000 0.08basis point * Trade Value USD 1.00
*
*/
const decimal bp = 0.01m;
if (monthlyTradeAmountInUSDollars <= 1000000000) // 1 billion
{
_commissionRate = 0.20m*bp;
_minimumOrderFee = 2.00m;
}
else if (monthlyTradeAmountInUSDollars <= 2000000000) // 2 billion
{
_commissionRate = 0.15m*bp;
_minimumOrderFee = 1.50m;
}
else if (monthlyTradeAmountInUSDollars <= 5000000000) // 5 billion
{
_commissionRate = 0.20m*bp;
_minimumOrderFee = 1.25m;
}
else
{
_commissionRate = 0.20m*bp;
_minimumOrderFee = 1.00m;
}
}
/// <summary>
/// Get the slippage approximation for this order
/// </summary>
/// <returns>Decimal value of the slippage approximation</returns>
/// <seealso cref="Order"/>
public override decimal GetSlippageApproximation(Security security, Order order)
{
//Return 0 by default
decimal slippage = 0.05m;
//For FOREX, the slippage is the Bid/Ask Spread for Tick, and an approximation for the
switch (security.Resolution)
{
case Resolution.Minute:
case Resolution.Second:
//Get the last data packet:
//Assume slippage is 1/10,000th of the price
slippage = security.GetLastData().Value*0.0002m;
break;
case Resolution.Tick:
var lastTick = (Tick) security.GetLastData();
switch (order.Direction)
{
case OrderDirection.Buy:
//We're buying, assume slip to Asking Price.
slippage = Math.Abs(order.Price - lastTick.AskPrice);
break;
case OrderDirection.Sell:
//We're selling, assume slip to the bid price.
slippage = Math.Abs(order.Price - lastTick.BidPrice);
break;
}
break;
}
return slippage;
}
/// <summary>
/// Get the fees from this order
/// </summary>
/// <param name="quantity">Quantity of purchase</param>
/// <param name="price">Price of the currency</param>
/// <remarks>
/// FXCM now uses a flat fee per trade instead of a spread model. This spread model is
/// out of date but the data has the spread built into historical data. >> New data source needed.
/// </remarks>
/// <returns>Decimal value of the order fee</returns>
public override decimal GetOrderFee(decimal quantity, decimal price)
{
var fee = _commissionRate*quantity*price;
return Math.Max(_minimumOrderFee, fee);
}
/// <summary>
/// Default implementation returns 0 for fees.
/// </summary>
/// <param name="security">The security matching the order</param>
/// <param name="order">The order to compute fees for</param>
/// <returns>The cost of the order in units of the account currency</returns>
public override decimal GetOrderFee(Security security, Order order)
{
var forex = (Forex) security;
// get the total order value in the account currency
var price = order.Status.IsFill() ? order.Price : security.Price;
var totalOrderValue = order.GetValue(price)*forex.QuoteCurrency.ConversionRate;
var fee = _commissionRate*totalOrderValue;
return Math.Max(_minimumOrderFee, fee);
}
}
}namespace QuantConnect
{
/*
* TimeSpanConsolidator Helper Routine: Assemble generic timespan bar lengths: e.g. 10 minutes:
*
* 1. Setup the new Consolidator class with the timespan period:
* var _consolidator = new Consolidator(TimeSpan.FromMinutes(10));
*
* 2. Add in the data with the update routine. It will return true when bar ready
* if (_consolidator.Update(data["MSFT"])) { UseBar }
*/
public class Consolidator
{
private TradeBar _resultBar;
private TradeBar _workingBar;
private DateTime _start;
private TimeSpan _period;
//Result:
public TradeBar Bar
{
get
{
return _resultBar;
}
}
//Constructor: Set the period we'd like to scan
public Consolidator(TimeSpan span)
{
this._period = span;
this._resultBar = new TradeBar();
this._workingBar = new TradeBar(new DateTime(), "", Decimal.Zero, Decimal.MinValue, Decimal.MaxValue, 0, 0);
}
//Submit this bar, return true if we've started a new one.
public bool Update(TradeBar newBar)
{
//Intialize:
if (_start == new DateTime())
{
_start = newBar.Time;
}
//While we're less than end date, keep adding to this bar:
if (newBar.Time < (_start + _period))
{
//Building bar:
AddToBar(newBar);
return false;
}
else
{
//Completed bar: start new one:
_resultBar = _workingBar;
//Create a new bar:
_workingBar = new TradeBar(newBar.Time, newBar.Symbol, Decimal.Zero, Decimal.MinValue, Decimal.MaxValue, 0, 0);
//Start of this bar:
_start = newBar.Time;
AddToBar(newBar);
return true;
}
}
//Add to a tradebar
private void AddToBar(TradeBar newBar)
{
//Add this data to working bar:
if (_workingBar.Time == new DateTime()) _workingBar.Time = newBar.Time;
if (_workingBar.Symbol == "") _workingBar.Symbol = newBar.Symbol;
if (_workingBar.Open == Decimal.Zero) _workingBar.Open = newBar.Open;
if (newBar.High > _workingBar.High) _workingBar.High = newBar.High;
if (newBar.Low < _workingBar.Low) _workingBar.Low = newBar.Low;
_workingBar.Close = newBar.Close;
_workingBar.Volume = newBar.Volume;
}
}
}