Skip to content
All library documents

Selecting High-Dollar-Volume Stocks with a Coarse Universe Filter

Article Strategy library · Author: QuantConnect

Summary

This example demonstrates a basic stock-universe selection process. At the coarse-selection stage, it ranks available securities by daily dollar volume and retains the three highest-ranked symbols. The algorithm requests daily data and starts with a specified cash balance and historical date window.

When the universe changes, it liquidates holdings in removed securities and assigns equal portfolio weights to newly added securities. The example records data, universe changes, and order events, but does not describe a fine fundamental filter despite the document’s introductory reference to coarse and fine selection. It also reports no backtest results or transaction-cost analysis. The selection rule focuses on liquidity as measured by dollar volume; it does not explain how to handle concentration, turnover, or portfolio risk beyond equal allocation among selected names.

Key ideas

  • The coarse universe ranks stocks by daily dollar volume and selects the three largest values.
  • Daily resolution is used for securities added to the universe.
  • Newly selected securities receive equal portfolio allocations, while invested securities removed from the universe are liquidated.
  • The example gives implementation mechanics but no evidence of investment performance or added fine fundamental screening.

Tags

Full text
# CoarseFundamentalTop3Algorithm


# CoarseFundamentalTop3Algorithm









Demonstration of using coarse and fine universe selection together to filter down a smaller universe of stocks.

## Source (Apache-2.0)

```python
# 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.

from AlgorithmImports import *

### <summary>
### Demonstration of using coarse and fine universe selection together to filter down a smaller universe of stocks.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="universes" />
### <meta name="tag" content="coarse universes" />
### <meta name="tag" content="fine universes" />
class CoarseFundamentalTop3Algorithm(QCAlgorithm):

    def initialize(self):
        '''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''

        self.set_start_date(2014,3,24)    #Set Start Date
        self.set_end_date(2014,4,7)      #Set End Date
        self.set_cash(50000)            #Set Strategy Cash

        # what resolution should the data *added* to the universe be?
        self.universe_settings.resolution = Resolution.DAILY

        # this add universe method accepts a single parameter that is a function that
        # accepts an IEnumerable<CoarseFundamental> and returns IEnumerable<Symbol>
        self.add_universe(self.coarse_selection_function)

        self.__number_of_symbols = 3
        self._changes = None


    # sort the data by daily dollar volume and take the top '__number_of_symbols'
    def coarse_selection_function(self, coarse):
        # sort descending by daily dollar volume
        sorted_by_dollar_volume = sorted(coarse, key=lambda x: x.dollar_volume, reverse=True)

        # return the symbol objects of the top entries from our sorted collection
        return [ x.symbol for x in sorted_by_dollar_volume[:self.__number_of_symbols] ]


    def on_data(self, data):

        self.log(f"OnData({self.utc_time}): Keys: {', '.join([key.value for key in data.keys()])}")

        # if we have no changes, do nothing
        if self._changes is None: return

        # liquidate removed securities
        for security in self._changes.removed_securities:
            if security.invested:
                self.liquidate(security.symbol)

        # we want 1/N allocation in each security in our universe
        for security in self._changes.added_securities:
            self.set_holdings(security.symbol, 1 / self.__number_of_symbols)

        self._changes = None


    # this event fires whenever we have changes to our universe
    def on_securities_changed(self, changes):
        self._changes = changes
        self.log(f"OnSecuritiesChanged({self.utc_time}):: {changes}")

    def on_order_event(self, fill):
        self.log(f"OnOrderEvent({self.utc_time}):: {fill}")

```

Shown in full with attribution under the source's licence. Licence: Apache-2.0

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.