The boilerplate for my code needs to do the following:
- Read a list of stocks to trade from a dropbox link
- Market order newly added symbols in the dropbox link and then create a trailing stop loss once the order fills
- Close any removed symbols the next day
I have all of this logic prepared but am running into two problems: I'm getting an access denied log which I assume is from the algo trying to access my dropbox link (even though I made the link public). I also am not sure how to submit a trailing stop loss order. I see it in the lean docs but it isn't clear how I submit that type of order.
Since the backtest returned an error, I can't seem to attach it to the post, so I am showing it below.
using QuantConnect.Algorithm;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Securities;
namespace QuantConnect
{
public class Earnings : QCAlgorithm
{
// the changes from the previous universe selection
private SecurityChanges _changes = SecurityChanges.None;
// only used in backtest for caching the file results
private readonly Dictionary<DateTime, List<string>> _backtestSymbolsPerDay = new Dictionary<DateTime, List<string>>();
List<Security> toOpen = new List<Security>();
List<Security> toClose = new List<Security>();
public override void Initialize()
{
// this sets the resolution for data subscriptions added by our universe
UniverseSettings.Resolution = Resolution.Tick;
// set our start and end for backtest mode
SetStartDate(2017, 8, 18);
SetEndDate(2017, 8, 24);
SetCash(30000);
// Schedule events
Schedule.On(DateRules.EveryDay("SPY"), TimeRules.AfterMarketOpen("SPY", 5), () => {OpenPositions();});
Schedule.On(DateRules.EveryDay("SPY"), TimeRules.AfterMarketOpen("SPY", 10), () => { ClosePositions(); });
// define a new custom universe that will trigger each day at midnight
AddUniverse("my-dropbox-universe", Resolution.Daily, dateTime =>
{
const string liveUrl = @"https://www.dropbox.com/s/yd7bzfsouzcphix/live_stocks.csv?dl=1";
const string backtestUrl = @"https://www.dropbox.com/s/151j6a4i8rpn37p/backtest_stocks.csv?dl=1";
var url = LiveMode ? liveUrl : backtestUrl;
using (var client = new WebClient())
{
// handle live mode file format
if (LiveMode)
{
// fetch the file from dropbox
var file = client.DownloadString(url);
// if we have a file for today, break apart by commas and return symbols
if (file.Length > 0) return file.ToCsv();
// no symbol today, leave universe unchanged
return Universe.Unchanged;
}
// backtest - first cache the entire file
if (_backtestSymbolsPerDay.Count == 0)
{
// fetch the file from dropbox only if we haven't cached the result already
var file = client.DownloadString(url);
// split the file into lines and add to our cache
foreach (var line in file.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries))
{
var csv = line.ToCsv();
var date = DateTime.ParseExact(csv[0], "yyyyMMdd", null);
var symbols = csv.Skip(1).ToList();
_backtestSymbolsPerDay[date] = symbols;
}
}
// if we have symbols for this date return them, else specify Universe.Unchanged
List<string> result;
if (_backtestSymbolsPerDay.TryGetValue(dateTime.Date, out result))
{
return result;
}
return Universe.Unchanged;
}
});
}
public void OnData(TradeBars data){}
public override void OnSecuritiesChanged(SecurityChanges changes) {
Debug("In on securities changed");
foreach (Security sec in changes.RemovedSecurities) {
if (sec.Invested) {
Debug(String.Format("Adding {0} to list for removal", sec.Symbol.ToString()));
toClose.Add(sec);
}
}
foreach (Security sec in changes.AddedSecurities){
Debug(String.Format("Adding {0} to list for opening", sec.Symbol.ToString()));
toOpen.Add(sec);
}
}
public void OpenPositions()
{
// Create Market Order and trailing stop order
var weighting = 1m / toOpen.Count;
foreach (Security sec in toOpen)
{
SetHoldings(sec.Symbol, weighting);
}
}
public override void OnOrderEvent(OrderEvent fill)
{
string message = String.Format("Order {0} {1} x {2} at {3} commission={4} OrderId={5}",
fill.Status.ToString(),
fill.FillQuantity,
fill.Symbol,
fill.FillPrice,
fill.OrderFee,
fill.OrderId);
Debug(message);
if (fill.Status.ToString() == "Filled")
{
Debug("How do I submit the trailing stop loss");
// LimitOrder(fill.Symbol, -1*fill.FillQuantity, [STOP TRAILING PERCENT], "TRAILINGSTOPLOSS");
}
}
public void ClosePositions()
{
// Close any remaining
foreach (Security sec in toClose)
{
SetHoldings(sec.Symbol, 0);
}
}
}
}
Sofyan Saputra
Got the dropbox link to work. Looks like when you write a schedule event, you have to add the equity you are using as reference to the scheduler explicitly even if the equity is in the universe you are creating. Now I am just having trouble with submitting a trailing stop loss order. I hope this can be done as an order type rather than having to do a workaround in OnData :).
I see trailing stop in this github file as well as an entry in the lean docs, so hopefully that means it is an available order type
Alexandre Catarino
Lean/QuantConnect doesn't have a Trailing Stop order that would be converted in the Interactive Brokers trailing order you pointed out. You can implement a trailing stop order in your algorithm. Once you have submitted the stop order, you will have a OrderTicket object that can be updated:
private OrderTicket _stopOrderTicket; // Creating an Order: _stopOrderTicket = StopMarketOrder("SPY", -100, 205); // Updating an Order: _stopOrderTicket.Update(new UpdateOrderFields{StopPrice = 207.50};
Sofyan Saputra
OK. Now I have a question about memory...My algorithm will look to have a universe of ~50 to 200 securities on any given day and may need to update stop orders constantly for up to 10 stocks. If I simulate the trailings top loss on a tick basis within OnData for these 10 securities, do you think I would run into memory or performance issues in live trading?
If I will, does that mean I have to create a less accurate trailing stop loss by updating the stop every minute or some larger time frame?
Jared Broad
That is fine Sofyan Saputra people only really have issues when plotting lots of charts (10+), massive rolling windows. Give it a go.. we display the server statistics in the live view so you can track it.
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
Sofyan Saputra
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!