namespace QuantConnect {
// Exit Manager handles scans for when posisions have hit targets and / or stops
public partial class SwingSystem : QCAlgorithm
{
public class ExitManager {
private SwingSystem _algorithm;
private Dictionary<string, decimal> ShortStops = new Dictionary<string, decimal>();
private Dictionary<string, decimal> LongStops = new Dictionary<string, decimal>();
private decimal _atrmultipler;
public ExitManager(SwingSystem algorithm) {
this._atrmultipler = algorithm.ATRmultiplier;
this._algorithm = algorithm;
foreach (string instrument in algorithm.Instruments) {
this.ShortStops.Add(instrument, Convert.ToDecimal(0));
this.LongStops.Add(instrument, Convert.ToDecimal(0));
}
}
public void UpdateStops(TradeBars bars) {
//Trailing ATR * 3 stops. Only move in direction of trade so losses are minimized and R is constant
foreach (string instrument in this._algorithm.Instruments) {
//Only update stops where there is no active position
if (this._algorithm.Portfolio[instrument].Quantity > 0) {
this.ShortStops[instrument] = bars[instrument].Price + this._algorithm.ATRs[instrument]*this._atrmultipler;
// if (bars[instrument].Price - this._algorithm.ATRs[instrument]*this._atrmultipler > this.LongStops[instrument]) {
// this.LongStops[instrument] = bars[instrument].Price - this._algorithm.ATRs[instrument]*this._atrmultipler;
// }
} else if (this._algorithm.Portfolio[instrument].Quantity < 0) {
this.LongStops[instrument] = bars[instrument].Price - this._algorithm.ATRs[instrument];
// if (bars[instrument].Price + this._algorithm.ATRs[instrument]*this._atrmultipler < this.ShortStops[instrument]) {
// this.ShortStops[instrument] = bars[instrument].Price + this._algorithm.ATRs[instrument]*this._atrmultipler;
// }
} else {
this.ShortStops[instrument] = bars[instrument].Price + this._algorithm.ATRs[instrument]*this._atrmultipler;
this.LongStops[instrument] = bars[instrument].Price - this._algorithm.ATRs[instrument]*this._atrmultipler;
}
}
}
public void ExecuteExits(TradeBars bars) {
foreach (string instrument in this._algorithm.Instruments) {
//If we're Long
if (this._algorithm.Portfolio[instrument].Quantity > 0) {
//Check if we've hit stops
if (bars[instrument].Low < this.LongStops[instrument]) {
this._algorithm.Log("Long stop loss reached at price " + bars[instrument].Price + " With stop loss at " + this.LongStops[instrument]);
this._algorithm.Liquidate(instrument);
}
//Check if RSI has returned to parity or price has crossed EMA
if (bars[instrument].High > this._algorithm.LongEmas[instrument] || this._algorithm.RSIs[instrument] > 50) {
this._algorithm.Liquidate(instrument);
this._algorithm.Log("Target reached");
}
}
//If we're short
if (this._algorithm.Portfolio[instrument].Quantity <0) {
//Check if we've hit stops
if (bars[instrument].High > this.ShortStops[instrument]) {
this._algorithm.Log("Short stop loss reached at price " + bars[instrument].Price + " With stop loss at " + this.ShortStops[instrument]);
this._algorithm.Liquidate(instrument);
}
//Check if RSI has returned to parity or price has crossed EMA
if (bars[instrument].Low < this._algorithm.LongEmas[instrument] || this._algorithm.RSIs[instrument] < 50) {
this._algorithm.Liquidate(instrument);
this._algorithm.Log("Target reached");
}
}
}
}
public decimal GetShortStop(string instrument) {
return this.ShortStops[instrument];
}
public decimal GetLongStop(string instrument) {
return this.LongStops[instrument];
}
}
}
}
namespace QuantConnect {
//
// Make sure to change "BasicTemplateAlgorithm" to your algorithm class name, and that all
// files use "public partial class" if you want to split up your algorithm namespace into multiple files.
//
public partial class SwingSystem : QCAlgorithm
{
public class EntryManager {
private SwingSystem _algorithm;
public EntryManager(SwingSystem algorithm) {
this._algorithm = algorithm;
}
public void UpdateBounds() {
}
public void ExecuteEntries(TradeBars bars, FilterManager filters) {
foreach (string instrument in this._algorithm.Instruments) {
if (this._algorithm.Portfolio[instrument].Quantity != 0) {
break;
}
//Check that the vol filter passes, that we have no current holdings before running entry check
//and that we are in a ranging market
if (filters.GetVolFilter(instrument) && this._algorithm.Portfolio[instrument].Quantity == 0 && filters.GetDaysInRange(instrument) >= 20) {
//If we're above the EMA + 3ATRS and the RSI has risen above 70, go short.
if (bars[instrument].Price > (this._algorithm.LongEmas[instrument] + (decimal)3 * this._algorithm.ATRs[instrument]) && this._algorithm.RSIs[instrument] > 70) {
this._algorithm.Log("Short order placed. RSI is " + this._algorithm.RSIs[instrument] + " Days in range is " + filters.GetDaysInRange(instrument));
this._algorithm.Order(instrument, -(this._algorithm.PositionSizes[instrument]));
}
//If we are below the EMA - 3ATRs and the RSI is below 30, go long
if (bars[instrument].Price < (this._algorithm.LongEmas[instrument] - (decimal)3 * this._algorithm.ATRs[instrument]) && this._algorithm.RSIs[instrument] < 30) {
this._algorithm.Log("Long order placed. RSI is " + this._algorithm.RSIs[instrument] + " Days in range is " + filters.GetDaysInRange(instrument));
this._algorithm.Order(instrument, this._algorithm.PositionSizes[instrument]);
}
}
}
}
}
}
}
namespace QuantConnect {
//
// Class to keep track of market conditions for each instrument so that only markets which are below
// a certain level of ATR volatility are traded.
public partial class SwingSystem : QCAlgorithm
{
public class FilterManager {
private SwingSystem _algorithm;
private Dictionary<string, int> TrendFilters = new Dictionary<string, int>();
private Dictionary<string, bool> VolFilters = new Dictionary<string, bool>();
private Dictionary<string, int> DaysInRange = new Dictionary<string, int>();
public int GetTrendFilter(string instrument) {
return this.TrendFilters[instrument];
}
public bool GetVolFilter(string instrument) {
return this.VolFilters[instrument];
}
public int GetDaysInRange(string instrument) {
return this.DaysInRange[instrument];
}
public FilterManager(SwingSystem algorithm) {
this._algorithm = algorithm;
foreach (string instrument in algorithm.Instruments) {
this.TrendFilters.Add(instrument, 0);
this.VolFilters.Add(instrument, false);
this.DaysInRange.Add(instrument, 0);
}
}
public void UpdateFilters(TradeBars bars) {
foreach (string instrument in this._algorithm.Instruments) {
this.VolFilters[instrument] = true;
//Check that the market is ranging
//We define this as the price being within 5% of the 100d EMA
if (bars[instrument].Price > this._algorithm.LongEmas[instrument]) {
if (bars[instrument].High < ((decimal)1.05 * this._algorithm.LongEmas[instrument])) {
this.DaysInRange[instrument] += 1;
} else {
if (this.DaysInRange[instrument] >= 20) {
this.DaysInRange[instrument] = 15;
} else if (this.DaysInRange[instrument] >= 5) {
this.DaysInRange[instrument] -= 5;
} else {
this.DaysInRange[instrument] = 0;
}
}
}
if (bars[instrument].Price < this._algorithm.LongEmas[instrument]) {
if (bars[instrument].Low > (this._algorithm.LongEmas[instrument] * (decimal)0.95)) {
this.DaysInRange[instrument] += 1;
} else {
if (this.DaysInRange[instrument] >= 20) {
this.DaysInRange[instrument] = 15;
} else if (this.DaysInRange[instrument] >= 5) {
this.DaysInRange[instrument] -= 5;
} else {
this.DaysInRange[instrument] = 0;
}
}
}
//Check if DaysInRange is in excess of 20
if (this.DaysInRange[instrument] >= 20) {
this.TrendFilters[instrument] = 1;
} else {
this.TrendFilters[instrument] = 0;
}
//Update vol filters
// if (this._algorithm.ATRs[instrument] / bars[instrument].Price < (decimal)0.02) {
// this.VolFilters[instrument] = true;
// }
// if (this._algorithm.ATRs[instrument] / bars[instrument].Price > (decimal)0.02) {
// this.VolFilters[instrument] = false;
// }
}
}
}
}
}
namespace QuantConnect {
//
// Make sure to change "BasicTemplateAlgorithm" to your algorithm class name, and that all
// files use "public partial class" if you want to split up your algorithm namespace into multiple files.
//
public partial class SwingSystem : QCAlgorithm
{
public class PositionSizeManager {
private SwingSystem _algorithm;
public PositionSizeManager(SwingSystem algorithm) {
this._algorithm = algorithm;
}
public void UpdatePositionSizes(TradeBars bars) {
//Position size is set so that 2% of capital is equivalent to a 1ATR move.
foreach (string instrument in this._algorithm.Instruments) {
var onepercent = this._algorithm.Portfolio.TotalPortfolioValue/50;
var atrs = bars[instrument].Price / (3*this._algorithm.ATRs[instrument]);
var positionsize = onepercent * atrs;
this._algorithm.PositionSizes[instrument] = (int)positionsize;
}
}
}
}
}
namespace QuantConnect
{
/*
*
* Swing system designed to only trade markets that have been within 5% of the 100d EMA for 20 days or more
*
*/
public partial class SwingSystem : QCAlgorithm
{
//Set up global variables
//Set all the assets we want to following
public List<string> Instruments = new List<string>();
public Dictionary<string, ExponentialMovingAverage> LongEmas = new Dictionary<string, ExponentialMovingAverage>();
public Dictionary<string, AverageTrueRange> ATRs = new Dictionary<string, AverageTrueRange>();
public Dictionary<string, int> PositionSizes = new Dictionary<string, int>();
public Dictionary<string, RelativeStrengthIndex> RSIs = new Dictionary<string, RelativeStrengthIndex>();
// set up objects
public PositionSizeManager positionSizeManager;
public ExitManager exitManager;
public EntryManager entryManager;
public FilterManager filterManager;
public decimal ATRmultiplier = Convert.ToDecimal(1);
//Initialize the data and resolution you require for your strategy:
public override void Initialize()
{
//initialise classes and fill instruments List
// Instruments.Add("GBPUSD");
// Instruments.Add("EURUSD");
// Instruments.Add("USDJPY");
Instruments.Add("USDCAD");
Instruments.Add("USDCHF");
// Instruments.Add("AUDUSD");
// Instruments.Add("NZDUSD");
exitManager = new ExitManager(this);
entryManager = new EntryManager(this);
positionSizeManager = new PositionSizeManager(this);
filterManager = new FilterManager(this);
SetWarmup(100);
foreach (string instrument in Instruments) {
AddForex(instrument, Resolution.Daily);
var longema = EMA(instrument, 100);
LongEmas.Add(instrument, longema);
var rsi = RSI(instrument, 10, MovingAverageType.Simple, Resolution.Daily);
RSIs.Add(instrument, rsi);
var atr = ATR(instrument, 20, MovingAverageType.Simple, Resolution.Daily);
ATRs.Add(instrument, atr);
PositionSizes.Add(instrument, 0);
}
//Start and End Date range for the backtest:
SetStartDate(2016, 01, 01);
SetEndDate(2016, 08, 01);
// SetEndDate(DateTime.Now.Date.AddDays(-1));
//Cash allocation
SetCash(1000000);
}
//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)
{
if (IsWarmingUp) {
return;
}
foreach (string instrument in Instruments) {
if (!LongEmas[instrument].IsReady) {
return;
}
if (!data.ContainsKey(instrument)) {
return;
}
}
positionSizeManager.UpdatePositionSizes(data);
filterManager.UpdateFilters(data);
entryManager.UpdateBounds();
exitManager.UpdateStops(data);
exitManager.ExecuteExits(data);
Schedule.On(DateRules.EveryDay(), TimeRules.AfterMarketOpen("USDCHF", 5), () =>
{
entryManager.ExecuteEntries(data, filterManager);
});
}
}
}