Weekly Portfolio Signal Export to CrunchDAO
Summary
This example shows how to connect a QuantConnect algorithm to CrunchDAO and submit portfolio targets on a weekly schedule. It reads a remote security skeleton to build a changing universe, tracks additions and removals, filters for securities with positive prices, and assigns equal weights to the remaining symbols. It then creates portfolio targets, optionally places corresponding holdings, and sends the targets through a signal export provider.
The scheduled routine checks that warm-up is complete and uses the calendar week number to avoid duplicate submissions during the same week. The example provides integration and scheduling mechanics rather than a tested trading signal: its weight calculation is a placeholder, and it contains no performance results, transaction-cost analysis, or risk controls. API credentials and model details must be supplied by the user, and the demonstration’s brief configured date range does not establish how the process behaves over longer periods or in live operation.
Key ideas
- The algorithm retrieves a custom universe and updates its tracked securities as membership changes.
- It assigns equal portfolio weights to securities with positive prices, using placeholder allocation logic.
- A scheduled routine waits for warm-up and submits targets no more than once per calendar week.
- The example separates portfolio target creation from exporting those targets to CrunchDAO.
- No evidence is provided about strategy profitability, execution costs, or live reliability.
Tags
Full text
# CrunchDAOSignalExportDemonstrationAlgorithm
# CrunchDAOSignalExportDemonstrationAlgorithm
This algorithm sends portfolio targets to CrunchDAO API once a week.
## 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>
### This algorithm sends portfolio targets to CrunchDAO API once a week.
### </summary>
### <meta name="tag" content="using data" />
### <meta name="tag" content="using quantconnect" />
### <meta name="tag" content="securities and portfolio" />
class CrunchDAOSignalExportDemonstrationAlgorithm(QCAlgorithm):
crunch_universe = []
def initialize(self) -> None:
self.set_start_date(2023, 5, 22)
self.set_end_date(2023, 5, 26)
self.set_cash(1_000_000)
# Disable automatic exports as we manually set them
self.signal_export.automatic_export_time_span = None
# Connect to CrunchDAO
api_key = "" # Your CrunchDAO API key
model = "" # The Id of your CrunchDAO model
submission_name = "" # A name for the submission to distinguish it from your other submissions
comment = "" # A comment for the submission
self.signal_export.add_signal_export_provider(CrunchDAOSignalExport(api_key, model, submission_name, comment))
self.set_security_initializer(BrokerageModelSecurityInitializer(self.brokerage_model, FuncSecuritySeeder(self.get_last_known_prices)))
# Add a custom data universe to read the CrunchDAO skeleton
self.add_universe(CrunchDaoSkeleton, "CrunchDaoSkeleton", Resolution.DAILY, self.select_symbols)
# Create a Scheduled Event to submit signals every monday before the market opens
self._week = -1
self.schedule.on(
self.date_rules.every([DayOfWeek.MONDAY, DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY, DayOfWeek.THURSDAY, DayOfWeek.FRIDAY]),
self.time_rules.at(13, 15, TimeZones.UTC),
self.submit_signals)
self.settings.minimum_order_margin_portfolio_percentage = 0
self.set_warm_up(timedelta(45))
def select_symbols(self, data: list[CrunchDaoSkeleton]) -> list[Symbol]:
return [x.symbol for x in data]
def on_securities_changed(self, changes: SecurityChanges) -> None:
for security in changes.removed_securities:
if security in self.crunch_universe:
self.crunch_universe.remove(security)
self.crunch_universe.extend(changes.added_securities)
def submit_signals(self) -> None:
if self.is_warming_up:
return
# Submit signals once per week
week_num = self.time.isocalendar()[1]
if self._week == week_num:
return
self._week = week_num
symbols = [security.symbol for security in self.crunch_universe if security.price > 0]
# Get historical price data
# close_prices = self.history(symbols, 22, Resolution.DAILY).close.unstack(0)
# Create portfolio targets
weight_by_symbol = {symbol: 1/len(symbols) for symbol in symbols} # Add your logic here
targets = [PortfolioTarget(symbol, weight) for symbol, weight in weight_by_symbol.items()]
# (Optional) Place trades
self.set_holdings(targets)
# Send signals to CrunchDAO
success = self.signal_export.set_target_portfolio(targets)
if not success:
self.debug(f"Couldn't send targets at {self.time}")
class CrunchDaoSkeleton(PythonData):
def get_source(self, config: SubscriptionDataConfig, date: datetime, is_live_mode: bool) -> SubscriptionDataSource:
return SubscriptionDataSource("https://tournament.crunchdao.com/data/skeleton.csv", SubscriptionTransportMedium.REMOTE_FILE)
def reader(self, config: SubscriptionDataConfig, line: str, date: datetime, is_live_mode: bool) -> DynamicData:
if not line[0].isdigit():
return None
skeleton = CrunchDaoSkeleton()
skeleton.symbol = config.symbol
try:
csv = line.split(',')
skeleton.end_time = datetime.strptime(csv[0], "%Y-%m-%d")
skeleton.symbol = Symbol(SecurityIdentifier.generate_equity(csv[1], Market.USA, mapping_resolve_date=skeleton.time), csv[1])
skeleton["Ticker"] = csv[1]
except ValueError:
# Do nothing
return None
return skeleton
```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.