using System;
using System.Collections.Generic;
namespace QuantConnect.Algorithm.CSharp {
public class AssetType {
string _etf = "UVXY,XIV,NUGT,DUST,JNUG,JDUST,LABU,LABD,GUSH,DRIP,TVIX,GASL,GASX,DWTI,UWTI,DGAZ,UGAZ,UBIO,ZBIO,BRZU,RUSS,SCO,UCO,RUSL,ERY,ERX,BIOL,SVXY,VXX,SILJ,BIB,BIS,VIXY,SOXL,VIIX,SOXS,BZQ,USLV,SLVP,DSLV,GDXJ,GLDX";
public string[] _forex = { "EURUSD", "GBPUSD", "USDCAD", "AUDUSD", "USDCHF" };
public string[] Forex {
get {
return _forex;
}
}
HashSet<string> _etfSet;
QCAlgorithm _algorithm;
public AssetType(QCAlgorithm algorithm) {
_algorithm = algorithm;
_etfSet = new HashSet<string>(_etf.Split(new string[1] { "," }, StringSplitOptions.RemoveEmptyEntries));
}
public bool isVolatileEtf(Symbol symbol) {
return _etfSet.Contains(symbol.Value);
}
}
}
using System;
using QuantConnect.Brokerages;
namespace QuantConnect.Algorithm.CSharp {
public class BackTestParameters {
QCAlgorithm _algorithm;
Resolution _resolution;
TimeSpan warmUpTime = TimeSpan.FromDays(5);
public BackTestParameters(QCAlgorithm algorithm, Resolution resolution) {
_algorithm = algorithm;
_resolution = resolution;
}
public void setUp() {
_algorithm.SetWarmUp(warmUpTime);
_algorithm.SetStartDate(2016, 9, 1);
_algorithm.SetEndDate(2016, 11, 7);
_algorithm.SetCash(25000);
_algorithm.UniverseSettings.Resolution = _resolution;
_algorithm.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);
}
public void setUpLocal() {
decimal cash = 25000;
_algorithm.SetWarmUp(warmUpTime);
_algorithm.SetStartDate(2015, 06, 01); //Set Start Date
_algorithm.SetEndDate(2016, 05, 01); //Set End Date
_algorithm.SetCash(cash);
_algorithm.SetBenchmark(delegate (DateTime dateTime) {
return cash;
});
_algorithm.UniverseSettings.Resolution = Resolution.Minute;
_algorithm.SetBrokerageModel(BrokerageName.FxcmBrokerage, AccountType.Margin);
}
}
}
using System;
using QuantConnect.Data.Market;
using QuantConnect.Indicators;
namespace QuantConnect.Algorithm.CSharp {
public class BounceSignal : TradeSignal {
int atrPeriod = 22;
decimal buyDiscount = 0.97m;
decimal sellPremium = 1.03m;
decimal stopPercentage = 0.02m;
decimal minimumAtrPercentage = 0.01m;
decimal adjustStopPrice = 0.01m;
AverageTrueRange _atr;
public BounceSignal(QCAlgorithm algorithm, Symbol symbol) : base(algorithm, symbol) {
_atr = algorithm.ATR(symbol, atrPeriod, MovingAverageType.Exponential, Resolution.Minute);
}
private decimal stopPrice(decimal lastPrice) {
return lastPrice - _atr / 2.0m;
}
public override void Scan(TradeBar bar) {
if (!_atr.IsReady) return;
if (TradeProfile == null) {
if (_atr / bar.Close > minimumAtrPercentage) {
var buyPrice = bar.Close * buyDiscount;
EntrySignal = new Signal(SignalType.Long, buyPrice, stopPrice(buyPrice));
}
} else if (TradeProfile.OpenTicket.Status == Orders.OrderStatus.Filled) {
//ExitSignal = new Signal(SignalType.Exit, _atr * sellPremium, 0);
var fillPrice = TradeProfile.OpenTicket.AverageFillPrice;
var lastStopPrice = Math.Max(EntrySignal.StopPrice, ExitSignal.StopPrice);
var newStopPrice = stopPrice(bar.Close);
if (newStopPrice > (1 + adjustStopPrice) * lastStopPrice) {
ExitSignal = new Signal(SignalType.AdjustStop, 0, newStopPrice);
} else {
ExitSignal = new Signal(SignalType.NoSignal, 0, lastStopPrice);
}
}
}
}
}
using System;
using QuantConnect.Data.Market;
using QuantConnect.Orders;
namespace QuantConnect.Algorithm.CSharp {
public class TradeProfile {
private decimal minimumPurchase = 500m;
//Ticket tracking the open order
public OrderTicket OpenTicket, StopTicket, ExitTicket;
//Keeps track of the current price and the direction of the trade
public Symbol Symbol { get; private set; }
TradeBar _lastBar;
Signal _entrySignal;
public Signal EntrySignal {
get {
return _entrySignal;
}
set {
_entrySignal = value;
}
}
private readonly int _tradeQuantity;
public int Quantity {
get {
if (EntrySignal.SignalType == SignalType.Long) {
return _tradeQuantity;
}
if (EntrySignal.SignalType == SignalType.Short) {
return -1 * _tradeQuantity;
}
return 0;
}
}
public TradeProfile(Symbol symbol, TradeBar lastBar, Signal entrySignal, decimal purchasingPower) {
Symbol = symbol;
_lastBar = lastBar;
EntrySignal = entrySignal;
_tradeQuantity = 0;
if (purchasingPower > minimumPurchase) {
_tradeQuantity = (int)(purchasingPower / entrySignal.BuyPrice);
}
}
internal void Update(TradeBar bar) {
if (bar.Symbol == Symbol) {
_lastBar = bar;
}
}
public bool isOpen() {
if (OpenTicket != null && OpenTicket.Status == OrderStatus.Filled) {
return !isClosed();
}
return false;
}
public bool isClosed() {
if (OpenTicket == null || OpenTicket.Status != OrderStatus.Filled) {
return false;
}
if (ExitTicket != null && ExitTicket.Status == OrderStatus.Filled) {
return true;
}
if (StopTicket != null && StopTicket.Status == OrderStatus.Filled) {
return true;
}
return false;
}
internal void AdjustStopPrice(decimal stopPrice) {
if (StopTicket != null) {
var updateOrderFields = new UpdateOrderFields();
updateOrderFields.StopPrice = stopPrice;
StopTicket.Update(updateOrderFields);
} else {
_entrySignal.StopPrice = stopPrice;
}
}
}
}
using QuantConnect.Data.Market;
using QuantConnect.Orders;
namespace QuantConnect.Algorithm.CSharp {
public enum SignalType {
Long = 1,
Short = -1,
Exit = 2,
AdjustStop = 3,
NoSignal = 0
}
public struct Signal {
public Signal(SignalType signalType, decimal buyPrice, decimal stopPrice) : this() {
SignalType = signalType;
BuyPrice = buyPrice;
StopPrice = stopPrice;
}
public SignalType SignalType { get; set; }
public decimal BuyPrice { get; set; }
public decimal StopPrice { get; set; }
}
public abstract class TradeSignal {
public Symbol Symbol { get; private set; }
public QCAlgorithm Algorithm { get; private set; }
public Signal EntrySignal { get; set; }
public Signal ExitSignal { get; set; }
public TradeProfile TradeProfile { get; set; }
public OrderDirection EntryDirection {
get {
if (EntrySignal.SignalType == SignalType.Long) {
return OrderDirection.Buy;
}
if (EntrySignal.SignalType == SignalType.Short) {
return OrderDirection.Sell;
}
return OrderDirection.Hold;
}
}
public TradeSignal(QCAlgorithm algorithm, Symbol symbol) {
Algorithm = algorithm;
Symbol = symbol;
EntrySignal = new Signal(SignalType.NoSignal, 0, 0);
ExitSignal = new Signal(SignalType.NoSignal, 0, 0);
TradeProfile = null;
}
public abstract void Scan(TradeBar bar);
}
}
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data.Market;
using QuantConnect.Orders;
using System;
using System.Diagnostics.Contracts;
namespace QuantConnect.Algorithm.CSharp {
class TradingAsset {
//public IExitSignal ExitSignal;
public TradeSignal _tradeSignal;
private readonly QCAlgorithm _algorithm;
Object orderLock = new Object();
TradeProfile _tradeProfile;
public Symbol Symbol { get; private set; }
decimal _maxTradeValue;
decimal _lastTradePrice;
decimal _lastTradeMarginPercent = 0.25m; // 50 %
public bool OpenOrders {
get {
return _tradeProfile != null;
}
}
public TradingAsset(QCAlgorithm algorithm, Symbol symbol, TradeSignal tradeSignal, decimal maxTradeValue) {
Symbol = symbol;
_tradeSignal = tradeSignal;
_algorithm = algorithm;
_maxTradeValue = maxTradeValue;
_tradeProfile = null;
_lastTradePrice = -1;
}
public void Scan(TradeBar bar) {
_tradeSignal.Scan(bar);
if (_tradeProfile != null) {
_tradeProfile.Update(bar);
}
ScanEntrySignal(bar);
ScanExitSignal(bar);
}
decimal buyingPower(OrderDirection direction) {
return Math.Min(_maxTradeValue, _algorithm.Portfolio.GetBuyingPower(Symbol, direction));
}
void ScanEntrySignal(TradeBar bar) {
if (_tradeProfile != null) return;
// debouncing to make sure trade don't happen in same price ranges
if (_lastTradePrice != -1) {
var high = _lastTradePrice * (1 + _lastTradeMarginPercent);
var low = _lastTradePrice * (1 - _lastTradeMarginPercent);
if (!(bar.Close > high || bar.Close < low)) {
return;
}
}
if (_tradeSignal.EntrySignal.SignalType == SignalType.Long
|| _tradeSignal.EntrySignal.SignalType == SignalType.Short) {
//Creates a new trade profile once it enters a trade
_tradeProfile = new TradeProfile(Symbol, bar, _tradeSignal.EntrySignal, buyingPower(_tradeSignal.EntryDirection));
if (_tradeProfile.Quantity != 0) {
lock (orderLock) {
_tradeProfile.OpenTicket = _algorithm.LimitOrder(Symbol, _tradeProfile.Quantity, _tradeSignal.EntrySignal.BuyPrice);
if (_tradeProfile.OpenTicket.Status != OrderStatus.Invalid) {
_tradeSignal.TradeProfile = _tradeProfile;
} else {
_tradeProfile = null;
}
}
}
}
}
void ScanExitSignal(TradeBar bar) {
if (_tradeProfile == null) return;
var exitSignal = _tradeSignal.ExitSignal;
if (exitSignal.SignalType != SignalType.NoSignal) {
Contract.Ensures(exitSignal.SignalType == SignalType.Exit
|| exitSignal.SignalType == SignalType.AdjustStop);
if (_tradeProfile.isOpen()) {
if (exitSignal.SignalType == SignalType.AdjustStop) {
_tradeProfile.AdjustStopPrice(exitSignal.StopPrice);
_algorithm.Log(string.Format("Adjusting Stop Order {0} {1}", Symbol, exitSignal.StopPrice));
} else {
var exitPrice = bar.Close;
if (exitSignal.BuyPrice > 0) {
exitPrice = exitSignal.BuyPrice;
}
_algorithm.Log(string.Format("Exit Ticket {0} ", Symbol));
if (_tradeProfile.ExitTicket != null) {
var updateOrderFields = new UpdateOrderFields();
updateOrderFields.LimitPrice = bar.Close;
_tradeProfile.ExitTicket.Update(updateOrderFields);
} else {
_tradeProfile.ExitTicket = _algorithm.LimitOrder(bar.Symbol, -1 * (int)_tradeProfile.OpenTicket.QuantityFilled, bar.Close);
}
}
}
}
}
public void OnOrderEvent(OrderEvent orderEvent) {
// This can be good way to avoid lots of loop but for now this is not working for me
lock (orderLock) {
if (orderEvent.Status == OrderStatus.Filled) {
_lastTradePrice = orderEvent.FillPrice;
if (orderEvent.OrderId == _tradeProfile.OpenTicket.OrderId) {
createStopOrder(_tradeProfile);
} else {
if (_tradeProfile.StopTicket != null && _tradeProfile.StopTicket.OrderId == orderEvent.OrderId) {
if (_tradeProfile.ExitTicket != null) {
_tradeProfile.ExitTicket.Cancel();
}
_tradeProfile = null;
return;
}
if (_tradeProfile.ExitTicket != null && _tradeProfile.ExitTicket.OrderId == orderEvent.OrderId) {
if (_tradeProfile.StopTicket != null) {
_tradeProfile.StopTicket.Cancel();
}
_tradeProfile = null;
return;
}
}
}
}
}
void createStopOrder(TradeProfile profile) {
if (profile.StopTicket != null) return;
//_algorithm.Log(string.Format("Creating Stop Order for {0}", profile.OpenTicket.OrderId));
var quantity = -1 * (int)profile.OpenTicket.QuantityFilled;
var stopPrice = profile.EntrySignal.StopPrice;
profile.StopTicket = _algorithm.StopMarketOrder(Symbol, quantity, stopPrice);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Securities.Forex;
namespace QuantConnect.Algorithm.CSharp {
public class UniverseSetup {
static int NumberOfSymbols = 100;
decimal minDollarVolume = 500000;
decimal maxDollarVolume = 10000000;
QCAlgorithm _algorithm;
AssetType _assetType;
Func<IEnumerable<Symbol>> _includeTradingAssets;
public UniverseSetup(QCAlgorithm algorithm, Func<IEnumerable<Symbol>> includeTradingAssets) {
_algorithm = algorithm;
_assetType = new AssetType(_algorithm);
_includeTradingAssets = includeTradingAssets;
}
public void setUpLocal() {
foreach (var security in _algorithm.Securities.Values) {
_algorithm.RemoveSecurity(security.Symbol);
}
foreach (string symbol in _assetType.Forex) {
_algorithm.AddSecurity(SecurityType.Forex, symbol, Resolution.Minute);
}
}
public void setUp() {
_algorithm.AddUniverse(CoarseSelectionFunction);
}
// sort the data by daily dollar volume and take the top 'NumberOfSymbols'
public IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse) {
var filterVolatileEtfs = coarse.Where(x => !_assetType.isVolatileEtf(x.Symbol));
var dollarVolumeFilter = filterVolatileEtfs.Where(
x => x.DollarVolume > minDollarVolume && x.DollarVolume < maxDollarVolume);
// sort descending by daily dollar volume
var sortedByDollarVolume = dollarVolumeFilter.OrderByDescending(x => x.DollarVolume);
// take the top entries from our sorted collection
var topN = sortedByDollarVolume.Take(NumberOfSymbols).Select(x => x.Symbol);
if (_includeTradingAssets != null) {
topN = topN.Union(_includeTradingAssets.Invoke());
}
return topN;
}
}
}
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System.Collections.Generic;
using QuantConnect.Data.Market;
using QuantConnect.Orders;
using QuantConnect.Securities;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Algorithm;
using QuantConnect.Algorithm.CSharp;
using System.Linq;
using System;
namespace QuantConnect {
public partial class QuantFramework : QCAlgorithm {
//Cap the investment maximum size ($).
public decimal maxTradeValueSizePerAsset = 5000;
private Resolution _dataResolution = Resolution.Minute;
private Dictionary<Symbol, TradingAsset> _tradingAssets = new Dictionary<Symbol, TradingAsset>();
BackTestParameters backTestParameters;
UniverseSetup universeSetup;
public override void Initialize() {
bool isLocal = false;
backTestParameters = new BackTestParameters(this, _dataResolution);
universeSetup = new UniverseSetup(this, IncludeTradingAssets);
if (isLocal) {
backTestParameters.setUpLocal();
universeSetup.setUpLocal();
} else {
backTestParameters.setUp();
universeSetup.setUp();
}
}
IEnumerable<Symbol> IncludeTradingAssets() {
return _tradingAssets.Values.Where(x => x.OpenOrders).Select(x => x.Symbol);
}
/// <summary>
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
/// </summary>
public void OnData(TradeBars data) {
if (IsWarmingUp) return;
foreach (var bar in data.Values) {
try {
if (_tradingAssets.ContainsKey(bar.Symbol)) {
_tradingAssets[bar.Symbol].Scan(bar);
}
} catch (Exception exp) {
Log(string.Format("Exception {0}\n{1}", exp, exp.StackTrace));
}
}
}
public override void OnSecuritiesChanged(SecurityChanges changes) {
foreach (Security s in changes.RemovedSecurities) {
if (_tradingAssets.ContainsKey(s.Symbol)) {
if (!_tradingAssets[s.Symbol].OpenOrders) {
_tradingAssets.Remove(s.Symbol);
}
}
}
foreach (Security s in changes.AddedSecurities) {
if (_tradingAssets.ContainsKey(s.Symbol)) {
continue;
}
_tradingAssets.Add(
s.Symbol,
new TradingAsset(this, s.Symbol, new BounceSignal(this, s.Symbol), maxTradeValueSizePerAsset));
}
}
public override void OnOrderEvent(OrderEvent orderEvent) {
_tradingAssets[orderEvent.Symbol].OnOrderEvent(orderEvent);
}
}
}