| Overall Statistics |
|
Total Trades 990 Average Win 0.23% Average Loss -0.22% Compounding Annual Return 0.195% Drawdown 4.100% Expectancy 0.004 Net Profit 0.196% Sharpe Ratio 0.052 Loss Rate 51% Win Rate 49% Profit-Loss Ratio 1.04 Alpha 0.003 Beta 0.001 Annual Standard Deviation 0.049 Annual Variance 0.002 Information Ratio -0.003 Tracking Error 0.14 Treynor Ratio 3.07 Total Fees $0.00 |
namespace QuantConnect {
// Tracking all the Trade information in a single class
public class TradeProfile
{
//Ticket tracking the open order
public OrderTicket OpenTicket, StopTicket;
//Keeps track of the current price and the direction of the trade
public decimal CurrentPrice;
public int TradeDirection;
private decimal _risk;
private int _maximumTradeQuantity;
protected decimal _volatility;
// Calclate the quantity based on the target risk in dollars.
public int Quantity
{
get
{
if (_volatility == 0) return 0;
long quantity = (long)(_risk / _volatility);
if (quantity > _maximumTradeQuantity) return _maximumTradeQuantity;
return (int)quantity;
}
}
//What is the stoploss move from current price
public decimal DeltaStopLoss
{
get
{
return _risk / Quantity;
}
}
// Calculates the Profit:Loss ratio
public decimal ProfitLossRatio
{
get
{
if(OpenTicket != null)
{
return OpenTicket.Quantity*(CurrentPrice - OpenTicket.AverageFillPrice) /_risk;
}
return 0m;
}
}
//Create a new tradeProfile and limit the maximum risk.
public TradeProfile(decimal volatility, decimal risk, decimal currentPrice, decimal maximumTradeSize)
{
_volatility = volatility;
_risk = risk;
CurrentPrice = currentPrice;
_maximumTradeQuantity = (int)(maximumTradeSize / CurrentPrice);
}
}
}namespace QuantConnect
{
public class BasicTemplateAlgorithm : QCAlgorithm
{
//Configure which securities you'd like to use:
public string[] Symbols = { "EURUSD", "GBPUSD"};
//Risk in dollars per trade ($ or the quote currency of the assets)
public decimal RiskPerTrade = 10;
//Sets the profit to loss ratio we want to hit before we exit
public decimal TargetProfitLossRatio = 1.2m;
//Roughly how long does our "alpha" take to run
public TimeSpan AverageInvestmentPeriod = TimeSpan.FromHours(6.5);
//Cap the investment maximum size ($).
public decimal MaximumTradeSize = 10000;
private Resolution _dataResolution = Resolution.Minute;
private Dictionary<Symbol, TradeProfile> _trades;
private Slice _data;
private Random random = new Random(131);
public override void Initialize()
{
SetStartDate(2015, 1, 1);
SetEndDate(2016, 01, 01);
SetCash(2500);
//Hold a trade profile per asset we're trading.
_trades = new Dictionary<Symbol, TradeProfile>();
//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, _dataResolution);
//Roughly 4c per 1000 traded, roughly 8c fees per trade.
Securities[symbol].FeeModel = new ConstantFeeModel(0.0m);
//Set our volatility model: the expected price movement for the day is 3 sigma of the standard deviation
Securities[symbol].VolatilityModel = new ThreeSigmaVolatilityModel(_dataResolution.ToTimeSpan(), (int)AverageInvestmentPeriod.TotalMinutes);
}
//Might but bug with the FXCM model? Fees way higher than normal?
// SetBrokerageModel(BrokerageName.FxcmBrokerage);
var dates = new[] { DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday };
// SUPER SECRET ALHA INDICATOR!!!!! Trade every day at 930, exit at 4pm :D
Schedule.On(DateRules.Every(dates), TimeRules.At(09, 30), () =>
{
foreach (var symbol in Symbols)
{
// ENTER THE MARKET RANDOM DIRECTION...!
if (Securities[symbol].VolatilityModel.Volatility == 0) continue;
_trades[symbol] = Enter(symbol, RandomOrderDirection());
}
});
Schedule.On(DateRules.Every(dates), TimeRules.At(16, 00), () =>
{
foreach (var symbol in Symbols)
{
// If the stoploss hasn't triggered, quit
if (!Portfolio[symbol].Invested) continue;
Exit(_trades[symbol]);
}
});
}
/// <summary>
/// Generate a random order direction for the "super secret alpha signal"
/// </summary>
private OrderDirection RandomOrderDirection()
{
if (random.NextDouble() > 0.5) return OrderDirection.Buy;
return OrderDirection.Sell;
}
/// <summary>
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
/// </summary>
public override void OnData(Slice data)
{
_data = data;
foreach (var symbol in Symbols)
{
if (_trades.ContainsKey(symbol) && _trades[symbol].OpenTicket.AverageFillPrice != 0)
{
_trades[symbol].CurrentPrice = data[symbol].Close;
if (_trades[symbol].ProfitLossRatio >= TargetProfitLossRatio)
{
//Exit the trade if we hit our profit to loss target
Exit(_trades[symbol]);
}
}
}
}
/// <summary>
/// Enter the market, for a given direction, and symbol: add a stop loss to the trade we make.
/// </summary>
public TradeProfile Enter(string symbol, OrderDirection orderDirection)
{
var price = _data[symbol].Close;
var trade = new TradeProfile(Securities[symbol].VolatilityModel.Volatility, RiskPerTrade, price, MaximumTradeSize);
trade.TradeDirection = (orderDirection == OrderDirection.Buy) ? 1 : -1;
trade.OpenTicket = MarketOrder(symbol, trade.TradeDirection * trade.Quantity);
// Attach a stop loss at the average fill price - stopPrice.
var stopPrice = trade.OpenTicket.AverageFillPrice - (trade.TradeDirection * trade.DeltaStopLoss);
trade.StopTicket = StopMarketOrder(symbol, -trade.TradeDirection * trade.Quantity, stopPrice);
return trade;
}
/// <summary>
/// Exit the market for a given trade profile
/// </summary>
public void Exit(TradeProfile trade)
{
Liquidate(trade.OpenTicket.Symbol);
if (trade.StopTicket != null && trade.StopTicket.Status != OrderStatus.Filled)
{
//If we haven't hit the stop loss then kill it.
trade.StopTicket.Cancel();
}
}
}
}using System;
using MathNet.Numerics.Statistics;
using QuantConnect.Data;
using QuantConnect.Indicators;
namespace QuantConnect {
public class ThreeSigmaVolatilityModel : IVolatilityModel {
private bool _needsUpdate;
private decimal _volatility;
private DateTime _lastUpdate;
private readonly TimeSpan _periodSpan;
private readonly object _sync = new object();
private readonly RollingWindow<double> _window;
/// <summary>
/// Gets the volatility of the security as a percentage
/// </summary>
public decimal Volatility
{
get
{
lock (_sync)
{
if (_window.Count < 2)
{
return 0m;
}
if (_needsUpdate)
{
_needsUpdate = false;
// volatility here is supposed to be a percentage
var std = _window.StandardDeviation().SafeDecimalCast();
_volatility = std * 3;
}
}
return _volatility;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="QuantConnect.Securities.RelativeStandardDeviationVolatilityModel"/> class
/// </summary>
/// <param name="periodSpan">The time span representing one 'period' length</param>
/// <param name="periods">The nuber of 'period' lengths to wait until updating the value</param>
public ThreeSigmaVolatilityModel(TimeSpan periodSpan, int periods)
{
if (periods < 2) throw new ArgumentOutOfRangeException("periods", "'periods' must be greater than or equal to 2.");
_periodSpan = periodSpan;
_window = new RollingWindow<double>(periods);
_lastUpdate = DateTime.MinValue + TimeSpan.FromMilliseconds(periodSpan.TotalMilliseconds * periods);
}
/// <summary>
/// Updates this model using the new price information in
/// the specified security instance
/// </summary>
/// <param name="security">The security to calculate volatility for</param>
/// <param name="data"></param>
public void Update(Security security, BaseData data)
{
var timeSinceLastUpdate = data.EndTime - _lastUpdate;
if (timeSinceLastUpdate >= _periodSpan)
{
lock (_sync)
{
_needsUpdate = true;
// we purposefully use security.Price for consistency in our reporting
// some streams of data will have trade/quote data, so if we just use
// data.Value we could be mixing and matching data streams
_window.Add((double)security.Price);
}
_lastUpdate = data.EndTime;
}
}
}
}