Hello, I receive this error message. Do't know how to add the data.
Failed to initialize algorithm: Initialize(): System.InvalidOperationException: Please implement GetSource(SubscriptionDataConfig, DateTime, bool) on your custom data type: CustomUniverse
Trying to implement this strategy:
Scan stocks for which the following conditions are true:
50-day SMA of SPY is above the 200-day SMA of SPY
20-day SMA of stock price is above the 50-day SMA of price
Buy when
Price has just crossed up through the eight-day SMA of the High.
Close when
Price has just crossed down through the eight-day SMA of the Low.
What is the best way to implement this strategy?
- Select stocks based on MovingAverages Crossing Overs (EMA20 > EM50)
- Buy if Close crosses ove SMA 8-day of Highs
Sell if Close crosses under SMA 8-day of Lows
Current code which causes an exception is atteched.
Help would be appreciated.
Ulrich
using System.Collections.Concurrent;
//using System.Linq;
//using QuantConnect.Data.Market;
//using QuantConnect.Data.UniverseSelection;
/*
In the Initialize() method set the fee model
AddSecurity(SecurityType.Equity."SPY");
Securities["SPY"].TransactionModel = new ConstantFeeTransActionModel(1);
*/
namespace QuantConnect
{
/*
Scan stocks for which the following conditions are true:
Price is above its 50-day average (SMA)
20-day SMA of price is above the 50-day SMA of price
50-day SMA of price is above the 200-day SMA of price
50-day average volume is greater than one million shares
Buy when
Price has just crossed up through the eight-day SMA of the High.
Close when
Price has just crossed down through the eight-day SMA of the Low.
*/
public class UW_SimpleStrategy : QCAlgorithm
{
private Symbol Universe = QuantConnect.Symbol.Create("LONG", SecurityType.Equity, Market.USA);
//Define required variables:
int quantity = 0;
decimal price = 0;
decimal tolerance = 1m; //0.1% safety margin in prices to avoid bouncing.
string symbolStr = "SPY";
DateTime sampledToday = DateTime.Now;
int number_in_Securities=0;
const decimal Tolerance = 0.01m;
bool BullMarket = false;
private SecurityChanges _changes = SecurityChanges.None;
private const decimal TargetPercent = 0.1m;
private readonly ConcurrentDictionary<Symbol, SelectionData> _averages = new ConcurrentDictionary<Symbol, SelectionData>();
private ExponentialMovingAverage ema_50;
class CustomUniverse : BaseData
{
public Symbol Symbol; // Asset symbol
public decimal Open; // Yesterday open price
public decimal High; // Yesterday high price
public decimal Low; // Yesterday low price
public decimal Price; // Yesterday close price
public decimal DollarVolume; // Traded shares x Price
public long Volume; // Traded shares
}
// class used to improve readability of the coarse selection function
private class SelectionData
//-------------------------------------------------------------------
{
public readonly ExponentialMovingAverage Fast;
public readonly ExponentialMovingAverage Slow;
public readonly SimpleMovingAverage hSMA;
public readonly SimpleMovingAverage lSMA;
public SelectionData()
{
Fast = new ExponentialMovingAverage(50);
Slow = new ExponentialMovingAverage(200);
hSMA = new SimpleMovingAverage(8); //SMA(security.Symbol, 8, Resolution.Daily, Field.High);
lSMA = new SimpleMovingAverage(8); // SMA(security.Symbol, 8, Resolution.Daily, Field.Low);
}
// computes an object score of how much large the fast is than the slow
public decimal ScaledDelta
{
get { return (Fast - Slow)/((Fast + Slow)/2m); }
}
// updates the EMA50 and EMA100 indicators, returning true when they're both ready
public bool Update(DateTime time, decimal value)
{
return Fast.Update(time, value) && Slow.Update(time, value);
}
// updates the SMA indicators, returning true when they're both ready
public bool Update_SMA(DateTime time, decimal value1, decimal value2)
{
return hSMA.Update(time, value1) && lSMA.Update(time, value2);
}
// returns uptrend or downtrend for stocks
public bool StockTrend(bool SpyTrend)
{
if (SpyTrend)
return Fast > Slow*(1 + Tolerance);
else
return Fast < Slow*(1 + Tolerance);
}
} // end of class
//Initialize the data and resolution you require for your strategy:
public override void Initialize()
{
SetStartDate(2014, 01, 01);
SetEndDate(DateTime.Now);
SetCash(25000);
// Subscriptions added via universe selection will have this resolution
UniverseSettings.Resolution = Resolution.Daily;
// Take the top 50 by dollar volume using coarse
// AddUniverse(Universe.DollarVolume.Top(50));
AddSecurity(SecurityType.Equity,"SPY",Resolution.Daily);
ema_50 = EMA("SPY", 50, Resolution.Daily);
BullMarket = ema_50 > Securities["SPY"].Close;
// if (ema_50.IsReady)
Debug ("\n+Init:" + " Ema50=" + ema_50.ToString() + " Close=" + Securities["SPY"].Close.ToString());
AddUniverse <CustomUniverse>("my-custom-universe", Resolution.Daily, data =>
{ Debug("in AddUniverse");
return from cLong in data
let bar = new TradeBar(Time, cLong.Symbol, cLong.Open, cLong.High, cLong.Low, cLong.Price,
cLong.Volume, TimeSpan.FromDays(1))//Resolution.Daily)
let selector = _averages.GetOrAdd(cLong.Symbol, sym => new SelectionData())
where cLong.Price > 10
// Update returns true when the indicators are ready,
// so don't accept until they are
where selector.Update(cLong.EndTime, cLong.Price)
where selector.Update_SMA(cLong.EndTime, cLong.High, cLong.Low)
// only pick symbols who have their 50 day ema
// over their 100 day ema
where selector.Fast > selector.Slow*(1 + Tolerance)
// Exclude SPY
where cLong.Symbol.ToString() != "SPY"
// price crossed over 8-day SMA of Low
where cLong.Price > selector.lSMA
orderby cLong.Volume descending
select cLong.Symbol;
});
} // end Initialize
//Handle TradeBar Events: a TradeBar occurs on every time-interval
public void OnData(TradeBars data) {
int i=0;
//Debug(Time.ToShortDateString() + " trace -1");
if (number_in_Securities != _changes.AddedSecurities.Count) {
Debug(Time.Date.ToShortDateString() + " Number=" + _changes.AddedSecurities.Count.ToString() );
number_in_Securities = _changes.AddedSecurities.Count;
}
//One data point per day:
// if (data.ContainsKey(symbolStr))
//if (sampledToday.Date == data[symbolStr].Time.Date) return;
//Only take one data point per day (opening price)
//price = Securities[symbolStr].Close;
// sampledToday = data[symbolStr].Time;
// Debug(Time.ToShortDateString() + " SPY Close= " + price.ToString());
// we'll simply go long each security we added to the universe
foreach (var security in _changes.AddedSecurities)
{
i++;
//SetHoldings(security.Symbol, TargetPercent);
//if (i < 30)
if (Time.Date == new DateTime(2017,10,21) )
Debug("Symbol= " + Time.ToShortDateString() + " " + security.Symbol.ToString());
if (Time.Date > new DateTime(2017,10,14) && Time.Date < new DateTime(2017,10,21))
Debug(Time.ToShortDateString() + " trace 1 " + security.Symbol.ToString());
//Get fresh cash balance: Set purchase quantity to equivalent 10% of portfolio.
decimal cash = Portfolio.Cash;
int holdings = (int)Portfolio[security.Symbol].Quantity;
//quantity = Convert.ToInt32((cash * 0.5m) / price);
quantity = 3;
if (holdings > 0 || holdings == 0) {
//If we're long, or flat: check if closed crossed under 8-day SMA Low:
// if (security.Close < (hSMA * (1-tolerance)) )
{
//Now go short: Short-EMA signals a negative turn: reverse holdings
Order(security.Symbol, -(holdings + quantity));
Log(Time.Date.ToShortDateString() + " > Go Short > Holdings: " + holdings.ToString() + " Quantity:" + quantity.ToString() + " Samples: " );
Debug(Time.Date.ToShortDateString() + " > Go Short > Holdings: " + holdings.ToString() + " Quantity:" + quantity.ToString() + " Samples: " );
}
} else if (holdings < 0 || holdings == 0) {
//If we're long, or flat: check if closed crossed over 8-day SMA High:
// if ( security.Close > (lSMA * (1+tolerance)))
{
//Now go long: Short-EMA crossed above long-EMA by sufficient margin
Order(security.Symbol, Math.Abs(holdings) + quantity);
Log(Time.Date.ToShortDateString() + "> Go Long > Holdings: " + holdings.ToString() + " Quantity:" + quantity.ToString() + " Samples: " );
Debug(Time.Date.ToShortDateString() + "> Go Long > Holdings: " + holdings.ToString() + " Quantity:" + quantity.ToString() + " Samples: " );
}
}
}
}
/// Event fired each time the we add/remove securities from the data feed
/// </summary>
/// <param name="changes">Object containing AddedSecurities and RemovedSecurities</param>
public override void OnSecuritiesChanged(SecurityChanges changes)
{
BullMarket = ema_50 > Securities["SPY"].Close;
_changes = changes;
}
//public override void OnEndOfDay() {
// if (!lSMA.IsReady) return;
// Plot("lSMA", lSMA);
// Plot("hSMA", hSMA);
//}
}
}
Alexandre Catarino
In the algorithm above, you are using the Universe Selection feature that is not required as it seems you only want to trade SPY at the moment.
For your goal, you only need to create 5 moving averages:
# In Initialize _ema200 = EMA("SPY", 200, Resolution.Daily); _ema050 = EMA("SPY", 50, Resolution.Daily); _ema020 = EMA("SPY", 20, Resolution.Daily); _ema8Hi = EMA("SPY", 8, Resolution.Daily, Field.High); _ema8Lo = EMA("SPY", 8, Resolution.Daily, Field.Low);
Then you will compare them in OnData event handler:
# In OnData(Slice slice) var price = slice["SPY"].Close; if (_ema020 > _ema050 && _ema050 > _ema200 && price > _ema200) { if (price > _ema8Hi) { SetHoldings("SPY", 1); } if (price < _ema8Lo) { Liquidate("SPY"); } }
From simple algorithm, we can implement a multi-asset equity one and finally an universe selection one.
Ulrich
I want to use the AddUniverse function to scan for stocks meeting following conditions: ema20 > ema50 > ema200. SPY shall be used to determine the overall trend. But I want to trade stocks.
Right now the main issue is the error message caused by an exception which I cannot solve.
Alexandre Catarino
That runtime exception is due to a custom universe election implementation.
The CustomUniverse class does not have all the necessary/mandatory methods. Please checkout this example.
Ulrich
Thank you Alex.
I checked out the example but it brings up several questions.
Should ww.wsj.com be used as a source for securities or is another source possible?
The file nyse-gainers.csv has not been updated.
In the class NyseTopGainers there are the methods wet, set, GetSource and Reader. How can I add the data for SMA of high, SMA of low and EMAs?
The discussion "RSI + SMA strategy - stocks filtered with coarse universe" looks promising in my understanding : RSI+SMA strategy
Do you think this approach could work also for filtering several securities by EMASs based on close data and SMAs based on high/low data.?
Regards
Ulrich
Alexandre Catarino
Since you were asking about the runtime error, I explained it to you.
However, I don't think you need custom universe. It is for particular cases where quants fetch their universe data from outside sources. In case, it will suffice QuantConnect's built-in Coarse Fundamental.
If you follow the link, you will see that Coarse Fundamental data does not have High and Low infomation, only Close (Price is closing price). I would advice you to get the EmaCrossUniverseSelectionAlgorithm and develop on top of it.
First, modify that algorithm to include the three EMA that use closing price:
private class SelectionData { public readonly ExponentialMovingAverage Fast; public readonly ExponentialMovingAverage Slow; public readonly ExponentialMovingAverage Slowest; public SelectionData() { Fast = new ExponentialMovingAverage(20); Slow = new ExponentialMovingAverage(50); Slowest = new ExponentialMovingAverage(200); } // computes an object score of how much large the fast is than the slow public decimal ScaledDelta { get { return (Fast - Slow)/((Fast + Slow)/2m); } } // updates the EMA20, EMA50 and EMA200 indicators, returning true when they're ready public bool Update(DateTime time, decimal value) { return Fast.Update(time, value) && Slow.Update(time, value) && Slowest.Update(time, value); } }
Then you will select the assets based on the conditions you stated above:
AddUniverse(coarse => { return (from cf in coarse // grab th SelectionData instance for this symbol let avg = _averages.GetOrAdd(cf.Symbol, sym => new SelectionData()) // Update returns true when the indicators are ready, so don't accept until they are where avg.Update(cf.EndTime, cf.Price) // only pick symbols who have their price over their 200 day ema where cf.Price > avg.Slowest // only pick symbols who have their 50 day ema over their 200 day ema where avg.Slow > avg.Slowest // only pick symbols who have their 20 day ema over their 20 day ema where avg.Fast> avg.Slow // prefer symbols with a larger delta by percentage between the two averages orderby avg.ScaledDelta descending // we only need to return the symbol and return 'Count' symbols select cf.Symbol).Take(Count); });
You will need to create a class similar to SelectionData, say SymbolData, and create a dictionary to hold its values Dictionary<Symbol, SymbolData> to save the SMA of High and Low to be used as trading signal.
Good luck!
Ulrich
Thank you. I managed some more steps but the issue now is to update the indicators.
I have created the class SymbolData and added the data to MySymbolData.
Dictionary<Symbol, SymbolData> MySymbolData = new Dictionary<Symbol, SymbolData>();
In OnSecuritiesChanged(SecurityChanges changes) the data are added to MySymbolData.
Trying in OnData(Slice data) to access the indicator values I noticed they are zero. Pobably not updated.
Debug(" >> " + Stock.Security + " " + Stock.Security.Price + " " + Stock.hSMA + " " + Stock.lSMA);
This statement shows zero for Stock.hSMA and Stock.lSMA.
How can I get the indicators updated?
BR
Ulrich
Ulrich
I woud appreciate very much if you could provide me some more guideline
on this issue.
thank you
Ulrich
Ulrich
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!