Back

Can ADX be used to filter a universe?

(I tried to filter for adx>30 and got no assets remaining - so I suspect i'm doing something wrong)

Update Backtest








We can use any indicator to filter a universe provided that the universe data has all the information the indicator needs.
For instance, if we are using QuantConnect's built in universe data, Coarse Universe Selection:

class CoarseFundamental {
public long Volume; // Traded shares
public decimal DollarVolume; // Traded shares x Price
public decimal Price; // Yesterday close price
public Symbol Symbol; // Asset symbol
}

we do not have OHLC data to compute an AverageDirectionalIndex (ADX) indicator.

In that case, we need to add the symbols to the universe first, compute the ADX, then select/rank the securities.

In the example below, after selecting the TOP10 DollarVolume symbols, I used History method to retrieve the latest 14 tradebars to compute ADX(14) and buy a security owhen its ADX is above 30.

0

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.


Many thanks for this example and explanation. It seems a bit more complicated than I anticipated, but I'll have a fresh look when I'm less exhausted :-)

Thanks!

0

ok - so i guess what' confusing me is this:

On another example around here I saw an ini function siilar to this:

private class SelectionData { // class used to improve readability of the coarse selection function
public readonly ExponentialMovingAverage Fast;
public readonly ExponentialMovingAverage Slow;
public readonly AverageDirectionalIndex adx;

public SelectionData() {
Fast = new ExponentialMovingAverage(9);
Slow = new ExponentialMovingAverage(21);
adx = new AverageDirectionalIndex("Daily",14);
}

public decimal ScaledDelta { // computes an object score of how much larger the fast is than the slow
get { return (Fast - Slow)/((Fast + Slow)/2m); } // calculate: diff between the emas divided by their avg.
}

public decimal adxOutput {
get { return adx; }
}

public bool Update(DateTime time, decimal value) { // updates the EMA indicators, returning true when they're both ready
return Fast.Update(time, value) && Slow.Update(time, value);
}
}

public override void Initialize() { // Initialise the data and resolution required, as well as the cash and start-end dates.
UniverseSettings.Leverage = 2.0m;
UniverseSettings.Resolution = Resolution.Daily;

SetStartDate(2010, 01, 01);
SetEndDate(2011, 01, 01);
SetCash(25*1000);

AddUniverse(coarse => {
return (from cLong in coarse
let avg = _averages.GetOrAdd(cLong.Symbol, sym => new SelectionData()) // grab the SelectionData instance for this symbol
where avg.Update(cLong.EndTime, cLong.Price) // Update returns true when the indicators are ready, so don't accept until they are
where avg.Fast > avg.Slow*(1 + Tolerance) // only pick symbols who have their fast ema over their slow ema
///where avg.adx > 30
where cLong.Price > 15 // only stocks with price > 15$
where cLong.Volume > 1000000 //
orderby avg.adxOutput descending // prefer strong-trending symbols, as indicated by adx.
select cLong.Symbol).Take(NumberOfSymbols); // we only need to return the symbol and return 'Count' symbols
});
}

except for the adx-related lines. So I guess i had expected to be able to apply the indicator right in the initialization process (avg refers to getting fast and slow ema) while in your code it is done onData.

when I tried to apply it in the init, like it is already doing for EMA, it didn't work (meaning that no stocks were selected), which is why i commented out the where adx line.

 

Due to my poor knowledge in both c# and quantconnect, i am forced to ask - is it possible to somehow apply adx filtering in a way similar to how this code filters by ema, or does it have to be that ema be applied during init and adx during onData?

 

0

In order to update the ADX indicator in the code above, we would need to include following code in the SymbolData class:

// updates the indicators, returning true when they're all ready
public bool Update(DateTime time, decimal value)
{
return
Fast.Update(time, value) && // Updates EMA(9)
Slow.Update(time, value) && // Updates EMA(21)
adx.Update(/*TradeBar object here*/); // Updates ADX(14)
}

However, you need to provide a tradebar to update the ADX, since the Average Directional Index (ADX) is tradebar indicator, meaning that we need at least 3 data points (high, low and closing price) to compute it.

This Update method is called inside the AddUniverse method:

AddUniverse(coarse =>
{
return (from cLong in coarse
/* ... */
// Update returns true when the indicators are ready,
//so don't accept until they are
where avg.Update(cLong.EndTime, cLong.Price)
/* ... */
});

where cLong is a CoarseFundamental object:

class CoarseFundamental {
public long Volume; // Traded shares
public decimal DollarVolume; // Traded shares x Price
public decimal Price; // Yesterday close price
public Symbol Symbol; // Asset symbol
}

As we can see, cLong does not have the required properties (open, high, low) to create a tradebar.

Therefore we need a custom universe that does, for example: 

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 long Volume; // Traded shares
}

in order to have the following in the AddUniverse:

AddUniverse<CustomUniverse>("my-custom-universe", Resolution.Daily, data =>
{
return (from cLong in data
let bar = new TradeBar(Time, cLong.Symbol,
cLong.Open, cLong.High, cLong.Low, cLong.Price,
cLong.Volume, Resolution.Daily)
/* ... */
// Update returns true when the indicators are ready,
//so don't accept until they are
where avg.Update(bar)
/* ... */
});

Finally, SymbolData.Update would have to be edited to:

// updates the indicators, returning true when they're all ready
public bool Update(TradeBar bar)
{
return
Fast.Update(bar.EndTime, bar.Price) && // Updates EMA(9)
Slow.Update(bar.EndTime, bar.Price) && // Updates EMA(21)
adx.Update(bar); // Updates ADX(14)
}

 

0

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.


Thank you for this very detailed explanation!

As I'm working to integrate it with the rest of my code, I am getting "Argument 8: cannot convert from 'QuantConnect.Resolution' to 'System.TimeSpan?'" for the "let bar = new TradeBar(Time, cLong.Symbol, cLong.Open, cLong.High, cLong.Low, cLong.Price, cLong.Volume, Resolution.Daily)" line.

This smells like a simple C# issue... but I don't know what to do with it.

0

Please use TimeSpan.FromDays(1):

AddUniverse<CustomUniverse>("my-custom-universe",
Resolution.Daily,
data =>
{
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))
/* ... */
// Update returns true when the indicators are ready,
//so don't accept until they are
where avg.Update(bar)
/* ... */
});


 

0

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.


Thanks again for the big help - that seems to cut it and compilation kinda passes - but now I am running into an issue with the custom universe it seems. 

After f​​​​ixing in the changes and derived changes to the best of my aunderstanding, I have this:

 

private class SelectionData { // class used to improve readability of the coarse selection function
public readonly ExponentialMovingAverage Fast;
public readonly ExponentialMovingAverage Slow;
public readonly AverageDirectionalIndex adx;

public SelectionData() {
Fast = new ExponentialMovingAverage(9);
Slow = new ExponentialMovingAverage(21);
adx = new AverageDirectionalIndex("Daily",14);
}

public decimal ScaledDelta { // computes an object score of how much larger the fast is than the slow
get { return (Fast - Slow)/((Fast + Slow)/2m); } // calculate: diff between the emas divided by their avg.
}

public decimal adxOutput {
get { return adx; }
}

public bool Update(TradeBar bar) { // updates the indicators, returning true when they're all ready
return
Fast.Update(bar.EndTime, bar.Price) && // Updates EMA(9)
Slow.Update(bar.EndTime, bar.Price) && // Updates EMA(21)
adx.Update(bar); // Updates ADX(14)
}
}

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 long Volume; // Traded shares
}

public override void Initialize() { // Initialise the data and resolution required, as well as the cash and start-end dates.
UniverseSettings.Leverage = 2.0m;
UniverseSettings.Resolution = Resolution.Daily;

SetStartDate(2010, 01, 01);
SetEndDate(2011, 01, 01);
SetCash(25*1000);

AddUniverse<CustomUniverse>("my-custom-universe", Resolution.Daily, data => {
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 = new SelectionData()
where selector.Update(bar) // Update returns true when the indicators are ready, so don't accept until they are
where selector.Fast > selector.Slow*(1 + Tolerance) // only pick symbols who have their fast ema over their slow ema
where selector.adx > 30
where cLong.Price > 15 // only stocks with price > 15$
where cLong.Volume > 1000000 // Volume > 100k
orderby selector.adx descending // prefer strong-trending symbols, as indicated by adx.
select cLong.Symbol).Take(NumberOfSymbols); // we only need to return the symbol and return 'Count' symbols
});
}

Which passes compilation with no errors but with warnings such as:

'EmaCrossUniverseSelectionAlgorithm.CustomUniverse.Symbol' hides inherited member 'BaseData.Symbol'. Use the new keyword if hiding was intended.

and

 Field 'EmaCrossUniverseSelectionAlgorithm.CustomUniverse.Volume' is never assigned to, and will always have its default value 0

Moving on to backtest despite the warnings, generates :

Failed to initialize algorithm: Initialize(): Please implement GetSource(SubscriptionDataConfig, DateTime, bool) on your custom data type: CustomUniverse

Am I missing some kind of a definition regarding the custom universe?

0

Here is a full example of a Custom Data Universe Algorithm.

0

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.


Sorry for the late followup but I was expanding my mind on another thread. One quick question though - does it mean that adx must work with an external source of data? or did I miss the point on this? I thought the QC data included all the  O/H/L/C data. if it does - I still didn't understand how can i use it with ADX...

0

Sorry Eyal, universe data does not have OHLC - just "C" close at the moment. If you'd like this you need to build the tradebar yourself or specify an external OHLC source. 

0

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.


By building the tradebar - what do you mean? how? up from the minute events, onData? I'm not sure how it's done. Is there a ready exacmple for that by any chance?

 

0

Seems like there's a tutorial for this in 2017: https://www.quantconnect.com/tutorials/consolidating-data-build-bars/

0

Update Backtest





0

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.


Loading...

This discussion is closed