From 942978b7adb6fc10a9f4f239d7017624955ef34e Mon Sep 17 00:00:00 2001
From: Alexandre Catarino This page explains how to use Hugging Face sentiment analysis models in LEAN trading algorithms. These models classify financial text into sentiment categories like positive, negative, and neutral. The following models are available: All of these models accept text input and return classification labels with confidence scores. You can use them with the Hugging Face
+ The following examples demonstrate usage of Hugging Face sentiment analysis models.
+
+ The following algorithm selects the most volatile asset at the beginning of each month.
+ It gets the Tiingo News articles that were released for the asset over the previous 10 days and then feeds them into a sentiment analysis model.
+ It aggregates the sentiment scores of all the news releases.
+ If the aggregated sentiment is positive, it enters a long position for the month.
+ If it's negative, it enters a short position.
+ You can replace the model name with any of the sentiment analysis models listed on the introduction page.
+ This page explains how to use Hugging Face fill-mask models in LEAN trading algorithms. Fill-mask models predict the most likely word to fill a masked position in a sentence. You can use them to extract text embeddings and build feature vectors from financial text. The following models are available: These models are useful for extracting text embeddings from financial news. You can feed these embeddings into a downstream classifier or use cosine similarity to measure the semantic similarity between documents.
+ The following examples demonstrate usage of Hugging Face fill-mask models for feature extraction.
+
+ The following algorithm selects a volatile asset at the beginning of each month.
+ It uses a fill-mask model to extract embeddings from Tiingo News articles.
+ It then compares the average embedding of recent news to a reference "bullish" and "bearish" embedding.
+ If the recent news is more similar to the bullish reference, it enters a long position.
+ You can replace the model name with any of the fill-mask models listed on the introduction page.
+ This page explains how to use Hugging Face text generation models in LEAN trading algorithms. These models generate text given an input prompt, which you can use for tasks like summarizing financial data or generating structured analysis. The following models are available: Text generation models can analyze market context and generate structured outputs. You can prompt them to classify market conditions or extract trading signals from financial text. Note that larger models like Gemma-7B and DeepSeek-70B require GPU nodes with sufficient memory.
+ The following examples demonstrate usage of Hugging Face text generation models.
+
+ The following algorithm uses GPT-2 to classify market conditions based on recent price data.
+ At the beginning of each month, it calculates trailing returns, volatility, and momentum for the universe of the 5 most liquid assets.
+ It then prompts GPT-2 to complete a structured market analysis template and parses the generated text to determine position sizing.
+ This page explains how to use Moirai in LEAN trading algorithms. The model repository provides the following description:
+ Moirai is a foundation model for universal time series forecasting. It is trained on the Large-scale Open Time Series Archive (LOTSA), a collection of 27 billion observations across 9 domains.
+ Moirai uses multiple input/output projection layers to handle varying frequencies and a mixture distribution to model the data generating process.
+ For details, refer to the paper Unified Training of Universal Time Series Forecasting Transformers.
+ The following model sizes are available:
+ The following examples demonstrate usage of the Moirai model.
+
+ The following algorithm selects the most liquid assets at the beginning of each month.
+ Once a quarter, it gets the trailing year of prices for all the assets in the universe and then forecasts the price paths over the upcoming quarter using Moirai.
+ It then uses the SciPy package to find the weights that maximize the future Sharpe ratio of the portfolio and rebalances the portfolio to those weights.
+ This page explains how to use MOMENT in LEAN trading algorithms. The model repository provides the following description:
+ MOMENT is a family of foundation models for general-purpose time series analysis. It supports multiple time series tasks including forecasting, classification, anomaly detection, and imputation out of the box.
+ MOMENT is pre-trained on a large collection of public time series data called the Time Series Pile, which encompasses diverse domains.
+ For details, refer to the paper MOMENT: A Family of Open Time-Series Foundation Models.
+
+ The following examples demonstrate usage of the MOMENT model.
+
+ The following algorithm selects the most liquid assets at the beginning of each month.
+ Once a quarter, it gets the trailing prices for all the assets in the universe and then forecasts the future price path using MOMENT.
+ It compares the forecasted price to the current price and goes long assets with a positive forecast and short assets with a negative forecast.
+ This page explains how to use Granite TTM in LEAN trading algorithms. The model repository provides the following description:
+ Granite-TimeSeries-TTM-R1 (TTM-R1) is a lightweight pre-trained model for time series forecasting. It is based on the decoder-style TSMixer architecture with adaptive patching and resolution prefix tuning.
+ TTM-R1 achieves state-of-the-art zero-shot forecasting while being significantly smaller than other foundation models for time series.
+ For details, refer to the paper Tiny Time Mixers (TTMs): Fast Pre-trained Models for Enhanced Zero/Few-Shot Forecasting of Multivariate Time Series.
+
+ The following examples demonstrate usage of the Granite TTM model.
+
+ The following algorithm selects the most liquid assets at the beginning of each month.
+ Once a quarter, it gets the trailing prices for all the assets in the universe and then forecasts the future price path using Granite TTM.
+ It compares the forecasted return to a threshold to decide position direction and weight.
+ This page explains how to use Chronos-Bolt in LEAN trading algorithms. The model repository provides the following description:
+ Chronos-Bolt models are a family of lightweight, efficient time series forecasting models. They are a follow-up to the original Chronos models, designed for faster inference and lower computational cost.
+ Chronos-Bolt uses a T5-based encoder-decoder architecture where the encoder processes the historical context and the decoder directly generates quantile forecasts.
+ Unlike the original Chronos models, Chronos-Bolt does not use tokenization, resulting in significantly faster inference.
+ For details, refer to the paper Chronos: Learning the Language of Time Series.
+
+ The following examples demonstrate usage of the Chronos-Bolt model.
+
+ The following algorithm selects the most liquid assets at the beginning of each month.
+ Once a quarter, it gets the trailing year of prices for all the assets in the universe and then forecasts the price paths over the upcoming quarter using Chronos-Bolt.
+ It then uses the SciPy package to find the weights that maximize the future Sharpe ratio of the portfolio and rebalances the portfolio to those weights.
+ Chronos-Bolt is faster than the original Chronos-T5 models because it directly generates quantile forecasts instead of sampling.
+
+
+
+transformers library to analyze the sentiment of financial news and social media posts, then use the results to inform trading decisions.
+ Example 1: News Sentiment Trading
+
+from transformers import pipeline, set_seed
+
+class SentimentAnalysisModelAlgorithm(QCAlgorithm):
+
+ def initialize(self):
+ self.set_start_date(2024, 9, 1)
+ self.set_end_date(2024, 12, 31)
+ self.set_cash(100_000)
+
+ self.universe_settings.resolution = Resolution.DAILY
+ self.universe_settings.schedule.on(self.date_rules.month_start("SPY"))
+ self._universe = self.add_universe(
+ lambda fundamental: [
+ self.history(
+ [f.symbol for f in sorted(
+ fundamental, key=lambda f: f.dollar_volume
+ )[-10:]],
+ timedelta(365), Resolution.DAILY
+ )['close'].unstack(0).pct_change().iloc[1:].std().idxmax()
+ ]
+ )
+
+ set_seed(1, True)
+
+ # Load the sentiment analysis pipeline.
+ # Replace the model name with any supported sentiment model.
+ self._sentiment_pipeline = pipeline(
+ "text-classification",
+ model="mrm8488/distilroberta-finetuned-financial-news-sentiment-analysis"
+ )
+
+ self._last_rebalance_time = datetime.min
+ self.set_warm_up(30, Resolution.DAILY)
+
+ def on_warmup_finished(self):
+ self._trade()
+ self.schedule.on(
+ self.date_rules.month_start("SPY", 1),
+ self.time_rules.midnight,
+ self._trade
+ )
+
+ def on_securities_changed(self, changes):
+ for security in changes.removed_securities:
+ self.remove_security(security.dataset_symbol)
+ for security in changes.added_securities:
+ security.dataset_symbol = self.add_data(
+ TiingoNews, security.symbol
+ ).symbol
+
+ def _trade(self):
+ if (self.is_warming_up or
+ self.time - self._last_rebalance_time < timedelta(14)):
+ return
+
+ # Get the target security.
+ security = self.securities[list(self._universe.selected)[0]]
+
+ # Get the latest news articles.
+ articles = self.history[TiingoNews](
+ security.dataset_symbol, 10, Resolution.DAILY
+ )
+ article_text = [
+ article.description for article in articles
+ if article.description
+ ]
+ if not article_text:
+ return
+
+ # Run sentiment analysis on each article.
+ # Truncate long articles to the model's max length.
+ results = self._sentiment_pipeline(
+ article_text, truncation=True, max_length=512
+ )
+
+ # Aggregate sentiment scores.
+ positive_score = 0
+ negative_score = 0
+ for result in results:
+ label = result['label'].lower()
+ score = result['score']
+ if 'pos' in label:
+ positive_score += score
+ elif 'neg' in label:
+ negative_score += score
+
+ self.plot("Sentiment", "Positive", positive_score)
+ self.plot("Sentiment", "Negative", negative_score)
+
+ # Rebalance based on sentiment.
+ weight = 1 if positive_score > negative_score else -0.25
+ self.set_holdings(
+ security.symbol, weight,
+ liquidate_existing_holdings=True
+ )
+ self._last_rebalance_time = self.time
+
+
+
+
+ Example 1: Embedding-Based News Similarity
+
+import torch
+import numpy as np
+from transformers import AutoTokenizer, AutoModel, set_seed
+
+class FillMaskEmbeddingAlgorithm(QCAlgorithm):
+
+ def initialize(self):
+ self.set_start_date(2024, 9, 1)
+ self.set_end_date(2024, 12, 31)
+ self.set_cash(100_000)
+
+ self.universe_settings.resolution = Resolution.DAILY
+ self.universe_settings.schedule.on(self.date_rules.month_start("SPY"))
+ self._universe = self.add_universe(
+ lambda fundamental: [
+ self.history(
+ [f.symbol for f in sorted(
+ fundamental, key=lambda f: f.dollar_volume
+ )[-10:]],
+ timedelta(365), Resolution.DAILY
+ )['close'].unstack(0).pct_change().iloc[1:].std().idxmax()
+ ]
+ )
+
+ set_seed(1, True)
+
+ # Load the model and tokenizer.
+ # Replace with any fill-mask model (e.g., google-bert/bert-base-uncased).
+ model_name = "distilbert/distilbert-base-uncased"
+ self._tokenizer = AutoTokenizer.from_pretrained(model_name)
+ self._model = AutoModel.from_pretrained(model_name)
+ self._model.eval()
+
+ # Create reference embeddings for bullish/bearish text.
+ self._bullish_embedding = self._get_embedding(
+ "Stock prices surged on strong earnings and revenue growth."
+ )
+ self._bearish_embedding = self._get_embedding(
+ "Stock prices plunged on weak earnings and declining revenue."
+ )
+
+ self._last_rebalance_time = datetime.min
+ self.set_warm_up(30, Resolution.DAILY)
+
+ def on_warmup_finished(self):
+ self._trade()
+ self.schedule.on(
+ self.date_rules.month_start("SPY", 1),
+ self.time_rules.midnight,
+ self._trade
+ )
+
+ def on_securities_changed(self, changes):
+ for security in changes.removed_securities:
+ self.remove_security(security.dataset_symbol)
+ for security in changes.added_securities:
+ security.dataset_symbol = self.add_data(
+ TiingoNews, security.symbol
+ ).symbol
+
+ def _get_embedding(self, text):
+ """Extract the [CLS] token embedding from the model."""
+ inputs = self._tokenizer(
+ text, return_tensors="pt", truncation=True, max_length=512
+ )
+ with torch.no_grad():
+ outputs = self._model(**inputs)
+ # Use the [CLS] token (first token) embedding.
+ return outputs.last_hidden_state[:, 0, :].squeeze().numpy()
+
+ def _cosine_similarity(self, a, b):
+ return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
+
+ def _trade(self):
+ if (self.is_warming_up or
+ self.time - self._last_rebalance_time < timedelta(14)):
+ return
+
+ # Get the target security.
+ security = self.securities[list(self._universe.selected)[0]]
+
+ # Get the latest news articles.
+ articles = self.history[TiingoNews](
+ security.dataset_symbol, 10, Resolution.DAILY
+ )
+ article_text = [
+ article.description for article in articles
+ if article.description
+ ]
+ if not article_text:
+ return
+
+ # Get embeddings for each article and average them.
+ embeddings = [self._get_embedding(text) for text in article_text]
+ avg_embedding = np.mean(embeddings, axis=0)
+
+ # Compare to reference embeddings.
+ bullish_sim = self._cosine_similarity(
+ avg_embedding, self._bullish_embedding
+ )
+ bearish_sim = self._cosine_similarity(
+ avg_embedding, self._bearish_embedding
+ )
+
+ self.plot("Similarity", "Bullish", bullish_sim)
+ self.plot("Similarity", "Bearish", bearish_sim)
+
+ # Rebalance based on similarity.
+ weight = 1 if bullish_sim > bearish_sim else -0.25
+ self.set_holdings(
+ security.symbol, weight,
+ liquidate_existing_holdings=True
+ )
+ self._last_rebalance_time = self.time
+
+
+
+
+ Example 1: GPT-2 Market Condition Classifier
+
+from transformers import pipeline, set_seed
+
+class GPT2MarketAnalysisAlgorithm(QCAlgorithm):
+
+ def initialize(self):
+ self.set_start_date(2024, 9, 1)
+ self.set_end_date(2024, 12, 31)
+ self.set_cash(100_000)
+
+ self.settings.min_absolute_portfolio_target_percentage = 0
+
+ set_seed(1, True)
+
+ # Load the text generation pipeline with GPT-2.
+ self._generator = pipeline(
+ "text-generation",
+ model="openai-community/gpt2"
+ )
+
+ # Define the universe.
+ spy = Symbol.create("SPY", SecurityType.EQUITY, Market.USA)
+ self.universe_settings.schedule.on(self.date_rules.month_start(spy))
+ self.universe_settings.resolution = Resolution.DAILY
+ self._universe = self.add_universe(
+ self.universe.top(
+ self.get_parameter('universe_size', 5)
+ )
+ )
+
+ self._last_rebalance = datetime.min
+ self.schedule.on(
+ self.date_rules.month_start(spy, 1),
+ self.time_rules.midnight,
+ self._trade
+ )
+ self.set_warm_up(timedelta(31))
+
+ def _trade(self):
+ if self.is_warming_up:
+ return
+ if self.time - self._last_rebalance < timedelta(25):
+ return
+ self._last_rebalance = self.time
+
+ symbols = list(self._universe.selected)
+ if not symbols:
+ return
+
+ # Get trailing 60-day price data.
+ history = self.history(
+ symbols, 60, Resolution.DAILY
+ )['close'].unstack(0)
+
+ scores = {}
+ for symbol in symbols:
+ prices = history[symbol].dropna()
+ if len(prices) < 20:
+ continue
+
+ # Calculate features.
+ returns_20d = (prices.iloc[-1] / prices.iloc[-20] - 1) * 100
+ volatility = prices.pct_change().std() * np.sqrt(252) * 100
+
+ # Create a structured prompt.
+ prompt = (
+ f"Stock analysis: 20-day return {returns_20d:.1f}%, "
+ f"annualized volatility {volatility:.1f}%. "
+ f"Market outlook:"
+ )
+
+ # Generate text.
+ result = self._generator(
+ prompt, max_new_tokens=30, num_return_sequences=1,
+ do_sample=True, temperature=0.7
+ )
+ generated = result[0]['generated_text'].lower()
+
+ # Parse sentiment from generated text.
+ bullish_words = ['bullish', 'growth', 'strong', 'positive', 'upward', 'buy', 'rally']
+ bearish_words = ['bearish', 'decline', 'weak', 'negative', 'downward', 'sell', 'crash']
+
+ bull_count = sum(1 for w in bullish_words if w in generated)
+ bear_count = sum(1 for w in bearish_words if w in generated)
+
+ # Combine model signal with momentum.
+ momentum_signal = 1 if returns_20d > 0 else -1
+ model_signal = bull_count - bear_count
+ scores[symbol] = momentum_signal + model_signal * 0.5
+
+ if not scores:
+ return
+
+ # Normalize scores to portfolio weights.
+ total = sum(abs(v) for v in scores.values())
+ if total == 0:
+ return
+ weights = {s: v / total for s, v in scores.items()}
+
+ # Rebalance.
+ self.set_holdings(
+ [
+ PortfolioTarget(symbol, weight)
+ for symbol, weight in weights.items()
+ ],
+ True
+ )
+
+
+
+
+
diff --git a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/09 Moirai/99 Examples.html b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/09 Moirai/99 Examples.html
new file mode 100644
index 0000000000..df0e985226
--- /dev/null
+++ b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/09 Moirai/99 Examples.html
@@ -0,0 +1,160 @@
+
+ Example 1: Price Prediction
+
+import torch
+import numpy as np
+from scipy.optimize import minimize
+from einops import rearrange
+from transformers import set_seed
+from uni2ts.model.moirai import MoiraiForecast, MoiraiModule
+from gluonts.dataset.pandas import PandasDataset
+# endregion
+
+class MoiraiTimeSeriesAlgorithm(QCAlgorithm):
+ """
+ This algorithm demonstrates how to use the Moirai time series
+ forecasting model. It forecasts the future equity curves of the
+ 5 most liquid assets, then finds portfolio weights that maximize
+ the future Sharpe ratio. The portfolio is rebalanced quarterly.
+ """
+
+ def initialize(self):
+ self.set_start_date(2024, 9, 1)
+ self.set_end_date(2024, 12, 31)
+ self.set_cash(100_000)
+
+ self.settings.min_absolute_portfolio_target_percentage = 0
+
+ set_seed(1, True)
+
+ # Load the pre-trained model.
+ self._device = "cuda" if torch.cuda.is_available() else "cpu"
+ self._model = MoiraiForecast(
+ module=MoiraiModule.from_pretrained(
+ "Salesforce/moirai-1.0-R-small"
+ ),
+ prediction_length=63, # ~3 months of trading days
+ context_length=252,
+ patch_size="auto",
+ num_samples=20,
+ )
+
+ # Define the universe.
+ spy = Symbol.create("SPY", SecurityType.EQUITY, Market.USA)
+ self.universe_settings.schedule.on(self.date_rules.month_start(spy))
+ self.universe_settings.resolution = Resolution.DAILY
+ self._universe = self.add_universe(
+ self.universe.top(
+ self.get_parameter('universe_size', 5)
+ )
+ )
+
+ # Define some trading parameters.
+ self._lookback_period = timedelta(
+ 365 * self.get_parameter('lookback_years', 1)
+ )
+ self._prediction_length = 63 # ~3 months of trading days
+
+ # Schedule rebalances.
+ self._last_rebalance = datetime.min
+ self.schedule.on(
+ self.date_rules.month_start(spy, 1),
+ self.time_rules.midnight,
+ self._trade
+ )
+ self.set_warmup(timedelta(31))
+
+ def _sharpe_ratio(
+ self, weights, returns, risk_free_rate,
+ trading_days_per_year=252):
+ mean_returns = returns.mean() * trading_days_per_year
+ cov_matrix = returns.cov() * trading_days_per_year
+ portfolio_return = np.sum(mean_returns * weights)
+ portfolio_std = np.sqrt(
+ np.dot(weights.T, np.dot(cov_matrix, weights))
+ )
+ sharpe_ratio = (portfolio_return - risk_free_rate) / portfolio_std
+ return -sharpe_ratio
+
+ def _optimize_portfolio(self, equity_curves):
+ returns = equity_curves.pct_change().dropna()
+ num_assets = returns.shape[1]
+ initial_guess = num_assets * [1. / num_assets]
+ result = minimize(
+ self._sharpe_ratio,
+ initial_guess,
+ args=(
+ returns,
+ self.risk_free_interest_rate_model.get_interest_rate(
+ self.time
+ )
+ ),
+ method='SLSQP',
+ bounds=tuple((0, 1) for _ in range(num_assets)),
+ constraints=(
+ {'type': 'eq', 'fun': lambda w: np.sum(w) - 1}
+ )
+ )
+ return result.x
+
+ def _trade(self):
+ if self.is_warming_up:
+ return
+ if self.time - self._last_rebalance < timedelta(80):
+ return
+ self._last_rebalance = self.time
+
+ symbols = list(self._universe.selected)
+
+ # Get historical prices.
+ history = self.history(
+ symbols, self._lookback_period
+ )['close'].unstack(0)
+
+ # Create a GluonTS dataset for each symbol.
+ forecasts = {}
+ for symbol in symbols:
+ prices = history[symbol].dropna()
+ if len(prices) < 60:
+ continue
+
+ # Prepare the dataset.
+ df = prices.to_frame('target')
+ df.index = pd.to_datetime(df.index)
+ df.index.freq = 'B' # Business day frequency
+ ds = PandasDataset(df, target='target')
+
+ # Generate forecasts.
+ predictor = self._model.create_predictor(batch_size=1)
+ forecast_it = predictor.predict(ds)
+ for forecast in forecast_it:
+ forecasts[symbol] = np.median(forecast.samples, axis=0)
+
+ if len(forecasts) < 2:
+ return
+
+ # Build a DataFrame of forecasted equity curves.
+ forecasts_df = pd.DataFrame(forecasts)
+
+ # Find optimal weights.
+ optimal_weights = self._optimize_portfolio(forecasts_df)
+
+ # Rebalance.
+ tradable_symbols = list(forecasts.keys())
+ self.set_holdings(
+ [
+ PortfolioTarget(symbol, optimal_weights[i])
+ for i, symbol in enumerate(tradable_symbols)
+ ],
+ True
+ )
+
+
diff --git a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/10 MOMENT/99 Examples.html b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/10 MOMENT/99 Examples.html
new file mode 100644
index 0000000000..31adcd1382
--- /dev/null
+++ b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/10 MOMENT/99 Examples.html
@@ -0,0 +1,119 @@
+
+ Example 1: Price Forecasting
+
+import torch
+import numpy as np
+from transformers import set_seed
+from momentfm import MOMENTPipeline
+# endregion
+
+class MomentTimeSeriesAlgorithm(QCAlgorithm):
+ """
+ This algorithm demonstrates how to use the MOMENT time series
+ foundation model. It forecasts future prices for the 5 most
+ liquid assets, then allocates the portfolio based on the
+ expected direction of each asset.
+ """
+
+ def initialize(self):
+ self.set_start_date(2024, 9, 1)
+ self.set_end_date(2024, 12, 31)
+ self.set_cash(100_000)
+
+ self.settings.min_absolute_portfolio_target_percentage = 0
+
+ set_seed(1, True)
+
+ # Load the pre-trained model.
+ self._model = MOMENTPipeline.from_pretrained(
+ "AutonLab/MOMENT-1-large",
+ model_kwargs={"task_name": "forecasting", "forecast_horizon": 63}
+ )
+ self._model.init()
+
+ # Define the universe.
+ spy = Symbol.create("SPY", SecurityType.EQUITY, Market.USA)
+ self.universe_settings.schedule.on(self.date_rules.month_start(spy))
+ self.universe_settings.resolution = Resolution.DAILY
+ self._universe = self.add_universe(
+ self.universe.top(
+ self.get_parameter('universe_size', 5)
+ )
+ )
+
+ self._lookback_period = timedelta(365)
+
+ # Schedule rebalances.
+ self._last_rebalance = datetime.min
+ self.schedule.on(
+ self.date_rules.month_start(spy, 1),
+ self.time_rules.midnight,
+ self._trade
+ )
+ self.set_warmup(timedelta(31))
+
+ def _trade(self):
+ if self.is_warming_up:
+ return
+ if self.time - self._last_rebalance < timedelta(80):
+ return
+ self._last_rebalance = self.time
+
+ symbols = list(self._universe.selected)
+
+ # Get historical prices.
+ history = self.history(
+ symbols, self._lookback_period
+ )['close'].unstack(0)
+
+ signals = {}
+ for symbol in symbols:
+ prices = history[symbol].dropna()
+ if len(prices) < 512:
+ continue
+
+ # MOMENT expects input of shape (batch, n_channels, seq_len).
+ # Use the last 512 observations as context.
+ context = prices.values[-512:]
+ input_tensor = torch.tensor(context).float().unsqueeze(0).unsqueeze(0)
+
+ # Generate forecasts.
+ with torch.no_grad():
+ output = self._model(input_tensor)
+
+ forecast = output.forecast.squeeze().numpy()
+
+ # Calculate the expected return from the forecast.
+ current_price = prices.iloc[-1]
+ forecasted_end_price = forecast[-1]
+ expected_return = (forecasted_end_price - current_price) / current_price
+ signals[symbol] = expected_return
+
+ if not signals:
+ return
+
+ # Allocate portfolio based on forecast direction.
+ long_assets = {s: v for s, v in signals.items() if v > 0}
+ short_assets = {s: v for s, v in signals.items() if v <= 0}
+
+ targets = []
+ if long_assets:
+ long_weight = 0.8 / len(long_assets)
+ for symbol in long_assets:
+ targets.append(PortfolioTarget(symbol, long_weight))
+ if short_assets:
+ short_weight = -0.2 / len(short_assets)
+ for symbol in short_assets:
+ targets.append(PortfolioTarget(symbol, short_weight))
+
+ self.set_holdings(targets, True)
+
+
diff --git a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/11 Granite TTM/99 Examples.html b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/11 Granite TTM/99 Examples.html
new file mode 100644
index 0000000000..95c01fbed5
--- /dev/null
+++ b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/11 Granite TTM/99 Examples.html
@@ -0,0 +1,132 @@
+
+ Example 1: Price Forecasting
+
+import torch
+import numpy as np
+from transformers import set_seed
+from tsfm_public import TinyTimeMixerForPrediction
+# endregion
+
+class GraniteTTMAlgorithm(QCAlgorithm):
+ """
+ This algorithm demonstrates how to use the Granite TTM time series
+ forecasting model. It forecasts future prices for the 5 most
+ liquid assets, then allocates the portfolio based on the
+ expected direction and magnitude of each forecast.
+ """
+
+ def initialize(self):
+ self.set_start_date(2024, 9, 1)
+ self.set_end_date(2024, 12, 31)
+ self.set_cash(100_000)
+
+ self.settings.min_absolute_portfolio_target_percentage = 0
+
+ set_seed(1, True)
+
+ # Load the pre-trained model.
+ self._model = TinyTimeMixerForPrediction.from_pretrained(
+ "ibm-granite/granite-timeseries-ttm-r1"
+ )
+ self._model.eval()
+
+ # Define the universe.
+ spy = Symbol.create("SPY", SecurityType.EQUITY, Market.USA)
+ self.universe_settings.schedule.on(self.date_rules.month_start(spy))
+ self.universe_settings.resolution = Resolution.DAILY
+ self._universe = self.add_universe(
+ self.universe.top(
+ self.get_parameter('universe_size', 5)
+ )
+ )
+
+ self._lookback_period = timedelta(365)
+
+ # Schedule rebalances.
+ self._last_rebalance = datetime.min
+ self.schedule.on(
+ self.date_rules.month_start(spy, 1),
+ self.time_rules.midnight,
+ self._trade
+ )
+ self.set_warmup(timedelta(31))
+
+ def _trade(self):
+ if self.is_warming_up:
+ return
+ if self.time - self._last_rebalance < timedelta(80):
+ return
+ self._last_rebalance = self.time
+
+ symbols = list(self._universe.selected)
+
+ # Get historical prices.
+ history = self.history(
+ symbols, self._lookback_period
+ )['close'].unstack(0)
+
+ signals = {}
+ for symbol in symbols:
+ prices = history[symbol].dropna()
+ if len(prices) < 512:
+ continue
+
+ # Normalize the price series.
+ price_values = prices.values[-512:]
+ mean_price = price_values.mean()
+ std_price = price_values.std()
+ if std_price == 0:
+ continue
+ normalized = (price_values - mean_price) / std_price
+
+ # TTM expects input of shape (batch, seq_len, n_channels).
+ input_tensor = torch.tensor(
+ normalized
+ ).float().unsqueeze(0).unsqueeze(-1)
+
+ # Generate forecasts.
+ with torch.no_grad():
+ output = self._model(input_tensor)
+
+ # Denormalize the forecast.
+ forecast = (
+ output.prediction_outputs.squeeze().numpy()
+ * std_price + mean_price
+ )
+
+ # Calculate the expected return from the forecast.
+ current_price = prices.iloc[-1]
+ forecasted_end_price = forecast[-1]
+ expected_return = (
+ (forecasted_end_price - current_price) / current_price
+ )
+ signals[symbol] = expected_return
+
+ if not signals:
+ return
+
+ # Allocate portfolio based on forecast direction.
+ long_assets = {s: v for s, v in signals.items() if v > 0}
+ short_assets = {s: v for s, v in signals.items() if v <= 0}
+
+ targets = []
+ if long_assets:
+ long_weight = 0.8 / len(long_assets)
+ for symbol in long_assets:
+ targets.append(PortfolioTarget(symbol, long_weight))
+ if short_assets:
+ short_weight = -0.2 / len(short_assets)
+ for symbol in short_assets:
+ targets.append(PortfolioTarget(symbol, short_weight))
+
+ self.set_holdings(targets, True)
+
+
diff --git a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/12 Chronos-Bolt/99 Examples.html b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/12 Chronos-Bolt/99 Examples.html
new file mode 100644
index 0000000000..4b85d355f2
--- /dev/null
+++ b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/12 Chronos-Bolt/99 Examples.html
@@ -0,0 +1,147 @@
+
+ Example 1: Price Prediction
+
+import torch
+import numpy as np
+from scipy.optimize import minimize
+from chronos import ChronosBoltPipeline
+from transformers import set_seed
+# endregion
+
+class ChronosBoltAlgorithm(QCAlgorithm):
+ """
+ This algorithm demonstrates how to use the Chronos-Bolt time
+ series forecasting model. It forecasts the future equity curves
+ of the 5 most liquid assets, then finds portfolio weights that
+ maximize the future Sharpe ratio. The portfolio is rebalanced
+ every 3 months.
+ """
+
+ def initialize(self):
+ self.set_start_date(2024, 9, 1)
+ self.set_end_date(2024, 12, 31)
+ self.set_cash(100_000)
+
+ self.settings.min_absolute_portfolio_target_percentage = 0
+
+ set_seed(1, True)
+
+ # Load the pre-trained Chronos-Bolt model.
+ self._pipeline = ChronosBoltPipeline.from_pretrained(
+ "autogluon/chronos-bolt-base",
+ device_map="cuda" if torch.cuda.is_available() else "cpu",
+ torch_dtype=torch.bfloat16,
+ )
+
+ # Define the universe.
+ spy = Symbol.create("SPY", SecurityType.EQUITY, Market.USA)
+ self.universe_settings.schedule.on(self.date_rules.month_start(spy))
+ self.universe_settings.resolution = Resolution.DAILY
+ self._universe = self.add_universe(
+ self.universe.top(
+ self.get_parameter('universe_size', 5)
+ )
+ )
+
+ self._lookback_period = timedelta(
+ 365 * self.get_parameter('lookback_years', 1)
+ )
+ self._prediction_length = 3 * 21 # Three months of trading days
+
+ # Schedule rebalances.
+ self._last_rebalance = datetime.min
+ self.schedule.on(
+ self.date_rules.month_start(spy, 1),
+ self.time_rules.midnight,
+ self._trade
+ )
+ self.set_warmup(timedelta(31))
+
+ def _sharpe_ratio(
+ self, weights, returns, risk_free_rate,
+ trading_days_per_year=252):
+ mean_returns = returns.mean() * trading_days_per_year
+ cov_matrix = returns.cov() * trading_days_per_year
+ portfolio_return = np.sum(mean_returns * weights)
+ portfolio_std = np.sqrt(
+ np.dot(weights.T, np.dot(cov_matrix, weights))
+ )
+ sharpe_ratio = (portfolio_return - risk_free_rate) / portfolio_std
+ return -sharpe_ratio
+
+ def _optimize_portfolio(self, equity_curves):
+ returns = equity_curves.pct_change().dropna()
+ num_assets = returns.shape[1]
+ initial_guess = num_assets * [1. / num_assets]
+ result = minimize(
+ self._sharpe_ratio,
+ initial_guess,
+ args=(
+ returns,
+ self.risk_free_interest_rate_model.get_interest_rate(
+ self.time
+ )
+ ),
+ method='SLSQP',
+ bounds=tuple((0, 1) for _ in range(num_assets)),
+ constraints=(
+ {'type': 'eq', 'fun': lambda w: np.sum(w) - 1}
+ )
+ )
+ return result.x
+
+ def _trade(self):
+ if self.is_warming_up:
+ return
+ if self.time - self._last_rebalance < timedelta(80):
+ return
+ self._last_rebalance = self.time
+
+ symbols = list(self._universe.selected)
+
+ # Get historical equity curves.
+ history = self.history(
+ symbols, self._lookback_period
+ )['close'].unstack(0)
+
+ # Forecast the future equity curves.
+ # Chronos-Bolt directly outputs quantile forecasts.
+ quantile_forecasts = self._pipeline.predict_quantiles(
+ [
+ torch.tensor(history[symbol].dropna())
+ for symbol in symbols
+ ],
+ self._prediction_length,
+ [0.1, 0.5, 0.9] # 10th, 50th, 90th percentiles
+ )
+
+ # Take the median forecast (index 1) for each asset.
+ forecasts_df = pd.DataFrame(
+ {
+ symbol: quantile_forecasts[i, :, 1].numpy()
+ for i, symbol in enumerate(symbols)
+ }
+ )
+
+ # Find the weights that maximize the forward Sharpe ratio.
+ optimal_weights = self._optimize_portfolio(forecasts_df)
+
+ # Rebalance the portfolio.
+ self.set_holdings(
+ [
+ PortfolioTarget(symbol, optimal_weights[i])
+ for i, symbol in enumerate(symbols)
+ ],
+ True
+ )
+
If it's negative, it enters a short position.
You can replace the model name with any of the sentiment analysis models listed on the introduction page.
from transformers import pipeline, set_seed class SentimentAnalysisModelAlgorithm(QCAlgorithm): diff --git a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/07 Fill-Mask/99 Examples.html b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/07 Fill-Mask/99 Examples.html index 397aa20f02..ca79c40812 100644 --- a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/07 Fill-Mask/99 Examples.html +++ b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/07 Fill-Mask/99 Examples.html @@ -11,7 +11,7 @@If the recent news is more similar to the bullish reference, it enters a long position. You can replace the model name with any of the fill-mask models listed on the introduction page. -
+import torch import numpy as np from transformers import AutoTokenizer, AutoModel, set_seed diff --git a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/08 Text Generation/99 Examples.html b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/08 Text Generation/99 Examples.html index 9200f94168..18a4e5841b 100644 --- a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/08 Text Generation/99 Examples.html +++ b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/08 Text Generation/99 Examples.html @@ -9,7 +9,7 @@At the beginning of each month, it calculates trailing returns, volatility, and momentum for the universe of the 5 most liquid assets. It then prompts GPT-2 to complete a structured market analysis template and parses the generated text to determine position sizing. -
+from transformers import pipeline, set_seed class GPT2MarketAnalysisAlgorithm(QCAlgorithm): diff --git a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/09 Moirai/99 Examples.html b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/09 Moirai/99 Examples.html index df0e985226..3dc8d821d4 100644 --- a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/09 Moirai/99 Examples.html +++ b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/09 Moirai/99 Examples.html @@ -9,17 +9,16 @@Once a quarter, it gets the trailing year of prices for all the assets in the universe and then forecasts the price paths over the upcoming quarter using Moirai. It then uses the SciPy package to find the weights that maximize the future Sharpe ratio of the portfolio and rebalances the portfolio to those weights. -
+import torch import numpy as np from scipy.optimize import minimize -from einops import rearrange from transformers import set_seed from uni2ts.model.moirai import MoiraiForecast, MoiraiModule from gluonts.dataset.pandas import PandasDataset # endregion -class MoiraiTimeSeriesAlgorithm(QCAlgorithm): +class MoiraiAlgorithm(QCAlgorithm): """ This algorithm demonstrates how to use the Moirai time series forecasting model. It forecasts the future equity curves of the @@ -36,13 +35,14 @@set_seed(1, True) - # Load the pre-trained model. + # Load the pre-trained Moirai model. self._device = "cuda" if torch.cuda.is_available() else "cpu" + self._prediction_length = 63 # ~3 months of trading days self._model = MoiraiForecast( module=MoiraiModule.from_pretrained( "Salesforce/moirai-1.0-R-small" ), - prediction_length=63, # ~3 months of trading days + prediction_length=self._prediction_length, context_length=252, patch_size="auto", num_samples=20, @@ -58,11 +58,9 @@
) ) - # Define some trading parameters. self._lookback_period = timedelta( 365 * self.get_parameter('lookback_years', 1) ) - self._prediction_length = 63 # ~3 months of trading days # Schedule rebalances. self._last_rebalance = datetime.min @@ -82,27 +80,21 @@
portfolio_std = np.sqrt( np.dot(weights.T, np.dot(cov_matrix, weights)) ) - sharpe_ratio = (portfolio_return - risk_free_rate) / portfolio_std - return -sharpe_ratio + return -(portfolio_return - risk_free_rate) / portfolio_std def _optimize_portfolio(self, equity_curves): returns = equity_curves.pct_change().dropna() num_assets = returns.shape[1] - initial_guess = num_assets * [1. / num_assets] result = minimize( self._sharpe_ratio, - initial_guess, + num_assets * [1. / num_assets], args=( returns, - self.risk_free_interest_rate_model.get_interest_rate( - self.time - ) + self.risk_free_interest_rate_model.get_interest_rate(self.time) ), method='SLSQP', bounds=tuple((0, 1) for _ in range(num_assets)), - constraints=( - {'type': 'eq', 'fun': lambda w: np.sum(w) - 1} - ) + constraints=({'type': 'eq', 'fun': lambda w: np.sum(w) - 1}) ) return result.x @@ -120,32 +112,25 @@
symbols, self._lookback_period )['close'].unstack(0) - # Create a GluonTS dataset for each symbol. + # Create a GluonTS dataset and generate forecasts. forecasts = {} + predictor = self._model.create_predictor(batch_size=len(symbols)) for symbol in symbols: prices = history[symbol].dropna() if len(prices) < 60: continue - - # Prepare the dataset. df = prices.to_frame('target') df.index = pd.to_datetime(df.index) - df.index.freq = 'B' # Business day frequency ds = PandasDataset(df, target='target') - - # Generate forecasts. - predictor = self._model.create_predictor(batch_size=1) - forecast_it = predictor.predict(ds) - for forecast in forecast_it: + for forecast in predictor.predict(ds): + # Take the median over samples. forecasts[symbol] = np.median(forecast.samples, axis=0) if len(forecasts) < 2: return - # Build a DataFrame of forecasted equity curves. + # Build forecasted equity curves and find optimal weights. forecasts_df = pd.DataFrame(forecasts) - - # Find optimal weights. optimal_weights = self._optimize_portfolio(forecasts_df) # Rebalance. diff --git a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/10 MOMENT/99 Examples.html b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/10 MOMENT/99 Examples.html index 31adcd1382..b29d846230 100644 --- a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/10 MOMENT/99 Examples.html +++ b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/10 MOMENT/99 Examples.html @@ -2,21 +2,21 @@ The following examples demonstrate usage of the MOMENT model.
- Example 1: Price Forecasting + Example 1: Price Prediction
The following algorithm selects the most liquid assets at the beginning of each month. Once a quarter, it gets the trailing prices for all the assets in the universe and then forecasts the future price path using MOMENT. It compares the forecasted price to the current price and goes long assets with a positive forecast and short assets with a negative forecast.
-+import torch import numpy as np from transformers import set_seed from momentfm import MOMENTPipeline # endregion -class MomentTimeSeriesAlgorithm(QCAlgorithm): +class MomentAlgorithm(QCAlgorithm): """ This algorithm demonstrates how to use the MOMENT time series foundation model. It forecasts future prices for the 5 most @@ -33,7 +33,7 @@set_seed(1, True) - # Load the pre-trained model. + # Load the pre-trained MOMENT model. self._model = MOMENTPipeline.from_pretrained( "AutonLab/MOMENT-1-large", model_kwargs={"task_name": "forecasting", "forecast_horizon": 63} @@ -82,9 +82,10 @@
continue # MOMENT expects input of shape (batch, n_channels, seq_len). - # Use the last 512 observations as context. context = prices.values[-512:] - input_tensor = torch.tensor(context).float().unsqueeze(0).unsqueeze(0) + input_tensor = torch.tensor( + context + ).float().unsqueeze(0).unsqueeze(0) # Generate forecasts. with torch.no_grad(): @@ -95,15 +96,17 @@
# Calculate the expected return from the forecast. current_price = prices.iloc[-1] forecasted_end_price = forecast[-1] - expected_return = (forecasted_end_price - current_price) / current_price + expected_return = ( + (forecasted_end_price - current_price) / current_price + ) signals[symbol] = expected_return if not signals: return # Allocate portfolio based on forecast direction. - long_assets = {s: v for s, v in signals.items() if v > 0} - short_assets = {s: v for s, v in signals.items() if v <= 0} + long_assets = [s for s, v in signals.items() if v > 0] + short_assets = [s for s, v in signals.items() if v <= 0] targets = [] if long_assets: diff --git a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/11 Granite TTM/99 Examples.html b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/11 Granite TTM/99 Examples.html index 95c01fbed5..c7ffc8dfc0 100644 --- a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/11 Granite TTM/99 Examples.html +++ b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/11 Granite TTM/99 Examples.html @@ -2,14 +2,14 @@ The following examples demonstrate usage of the Granite TTM model.
- Example 1: Price Forecasting + Example 1: Price Prediction
The following algorithm selects the most liquid assets at the beginning of each month. Once a quarter, it gets the trailing prices for all the assets in the universe and then forecasts the future price path using Granite TTM. - It compares the forecasted return to a threshold to decide position direction and weight. + It compares the forecasted return to the current price and allocates the portfolio based on forecast direction.
-+import torch import numpy as np from transformers import set_seed @@ -33,7 +33,7 @@set_seed(1, True) - # Load the pre-trained model. + # Load the pre-trained Granite TTM model. self._model = TinyTimeMixerForPrediction.from_pretrained( "ibm-granite/granite-timeseries-ttm-r1" ) @@ -105,7 +105,7 @@
# Calculate the expected return from the forecast. current_price = prices.iloc[-1] - forecasted_end_price = forecast[-1] + forecasted_end_price = float(forecast[-1]) expected_return = ( (forecasted_end_price - current_price) / current_price ) @@ -115,8 +115,8 @@
return # Allocate portfolio based on forecast direction. - long_assets = {s: v for s, v in signals.items() if v > 0} - short_assets = {s: v for s, v in signals.items() if v <= 0} + long_assets = [s for s, v in signals.items() if v > 0] + short_assets = [s for s, v in signals.items() if v <= 0] targets = [] if long_assets: diff --git a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/12 Chronos-Bolt/99 Examples.html b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/12 Chronos-Bolt/99 Examples.html index 4b85d355f2..f56403d815 100644 --- a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/12 Chronos-Bolt/99 Examples.html +++ b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/12 Chronos-Bolt/99 Examples.html @@ -8,9 +8,9 @@
The following algorithm selects the most liquid assets at the beginning of each month. Once a quarter, it gets the trailing year of prices for all the assets in the universe and then forecasts the price paths over the upcoming quarter using Chronos-Bolt. It then uses the SciPy package to find the weights that maximize the future Sharpe ratio of the portfolio and rebalances the portfolio to those weights. - Chronos-Bolt is faster than the original Chronos-T5 models because it directly generates quantile forecasts instead of sampling. + Chronos-Bolt is a faster variant of Chronos-T5 that directly generates quantile forecasts. -
+import torch import numpy as np from scipy.optimize import minimize @@ -114,21 +114,22 @@symbols, self._lookback_period )['close'].unstack(0) - # Forecast the future equity curves. - # Chronos-Bolt directly outputs quantile forecasts. - quantile_forecasts = self._pipeline.predict_quantiles( + # Forecast the future equity curves using Chronos-Bolt. + # predict() returns (num_series, num_samples, prediction_length). + all_forecasts = self._pipeline.predict( [ torch.tensor(history[symbol].dropna()) for symbol in symbols ], - self._prediction_length, - [0.1, 0.5, 0.9] # 10th, 50th, 90th percentiles + self._prediction_length ) - # Take the median forecast (index 1) for each asset. + # Take the median forecast for each asset. forecasts_df = pd.DataFrame( { - symbol: quantile_forecasts[i, :, 1].numpy() + symbol: np.quantile( + all_forecasts[i].numpy(), 0.5, axis=0 + ) for i, symbol in enumerate(symbols) } ) From 0dab9022fd5c12c9b2848d6b10265c3d324958ba Mon Sep 17 00:00:00 2001 From: Alexandre Catarino
Date: Tue, 17 Mar 2026 22:12:43 +0000 Subject: [PATCH 3/6] Remove Moirai, MOMENT, Granite TTM examples; fix 00.json Chronos-Bolt entry (#2102) - Delete 09 Moirai, 10 MOMENT, 11 Granite TTM sections (require uni2ts/momentfm/tsfm_public, not available in QC Cloud; tracked in QuantConnect/Lean#9340) - Fix 00.json: correct Chronos-Bolt folder key from "09" to "12" - Remove example links for Moirai, MOMENT, Granite TTM rows in hugging-face-table.html Co-Authored-By: Claude Opus 4.6 --- .../04 Hugging Face/02 Popular Models/00.json | 3 - .../09 Moirai/01 Introduction.html | 16 -- .../09 Moirai/99 Examples.html | 145 ------------------ .../02 Popular Models/09 Moirai/metadata.json | 12 -- .../10 MOMENT/01 Introduction.html | 9 -- .../10 MOMENT/99 Examples.html | 122 --------------- .../02 Popular Models/10 MOMENT/metadata.json | 12 -- .../11 Granite TTM/01 Introduction.html | 9 -- .../11 Granite TTM/99 Examples.html | 132 ---------------- .../11 Granite TTM/metadata.json | 12 -- .../machine-learning/hugging-face-table.html | 10 +- 11 files changed, 5 insertions(+), 477 deletions(-) delete mode 100644 03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/09 Moirai/01 Introduction.html delete mode 100644 03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/09 Moirai/99 Examples.html delete mode 100644 03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/09 Moirai/metadata.json delete mode 100644 03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/10 MOMENT/01 Introduction.html delete mode 100644 03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/10 MOMENT/99 Examples.html delete mode 100644 03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/10 MOMENT/metadata.json delete mode 100644 03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/11 Granite TTM/01 Introduction.html delete mode 100644 03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/11 Granite TTM/99 Examples.html delete mode 100644 03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/11 Granite TTM/metadata.json diff --git a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/00.json b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/00.json index 13b8b3714b..8de9478bae 100644 --- a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/00.json +++ b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/00.json @@ -11,9 +11,6 @@ "06" : "Text classification", "07" : "Feature extraction", "08" : "Text generation", - "09" : "Time series forecasting", - "10" : "Time series forecasting", - "11" : "Time series forecasting", "12" : "Time series forecasting" } } \ No newline at end of file diff --git a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/09 Moirai/01 Introduction.html b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/09 Moirai/01 Introduction.html deleted file mode 100644 index 09cfb91aff..0000000000 --- a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/09 Moirai/01 Introduction.html +++ /dev/null @@ -1,16 +0,0 @@ - This page explains how to use Moirai in LEAN trading algorithms. The model repository provides the following description:
- --- -- Moirai is a foundation model for universal time series forecasting. It is trained on the Large-scale Open Time Series Archive (LOTSA), a collection of 27 billion observations across 9 domains. - Moirai uses multiple input/output projection layers to handle varying frequencies and a mixture distribution to model the data generating process. - For details, refer to the paper Unified Training of Universal Time Series Forecasting Transformers. -
-The following model sizes are available:
--
diff --git a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/09 Moirai/99 Examples.html b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/09 Moirai/99 Examples.html deleted file mode 100644 index 3dc8d821d4..0000000000 --- a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/09 Moirai/99 Examples.html +++ /dev/null @@ -1,145 +0,0 @@ -- Salesforce/moirai-1.0-R-small — Small variant (14M parameters)
-- Salesforce/moirai-1.0-R-base — Base variant (91M parameters)
-- Salesforce/moirai-1.0-R-large — Large variant (311M parameters)
-- The following examples demonstrate usage of the Moirai model. -
-- Example 1: Price Prediction -
-- The following algorithm selects the most liquid assets at the beginning of each month. - Once a quarter, it gets the trailing year of prices for all the assets in the universe and then forecasts the price paths over the upcoming quarter using Moirai. - It then uses the SciPy package to find the weights that maximize the future Sharpe ratio of the portfolio and rebalances the portfolio to those weights. -
--diff --git a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/09 Moirai/metadata.json b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/09 Moirai/metadata.json deleted file mode 100644 index 540340ba5d..0000000000 --- a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/09 Moirai/metadata.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "type": "metadata", - "values": { - "description": "This page explains how to use Moirai time series forecasting models in LEAN trading algorithms.", - "keywords": "time series forecasting model, Moirai, pre-trained AI model, price prediction, free AI models", - "og:description": "This page explains how to use Moirai time series forecasting models in LEAN trading algorithms.", - "og:title": "Moirai - Documentation QuantConnect.com", - "og:type": "website", - "og:site_name": "Moirai - QuantConnect.com", - "og:image": "https://cdn.quantconnect.com/docs/i/writing-algorithms/machine-learning/hugging-face/popular-models/moirai.png" - } -} diff --git a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/10 MOMENT/01 Introduction.html b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/10 MOMENT/01 Introduction.html deleted file mode 100644 index 76232fb0cc..0000000000 --- a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/10 MOMENT/01 Introduction.html +++ /dev/null @@ -1,9 +0,0 @@ -import torch -import numpy as np -from scipy.optimize import minimize -from transformers import set_seed -from uni2ts.model.moirai import MoiraiForecast, MoiraiModule -from gluonts.dataset.pandas import PandasDataset -# endregion - -class MoiraiAlgorithm(QCAlgorithm): - """ - This algorithm demonstrates how to use the Moirai time series - forecasting model. It forecasts the future equity curves of the - 5 most liquid assets, then finds portfolio weights that maximize - the future Sharpe ratio. The portfolio is rebalanced quarterly. - """ - - def initialize(self): - self.set_start_date(2024, 9, 1) - self.set_end_date(2024, 12, 31) - self.set_cash(100_000) - - self.settings.min_absolute_portfolio_target_percentage = 0 - - set_seed(1, True) - - # Load the pre-trained Moirai model. - self._device = "cuda" if torch.cuda.is_available() else "cpu" - self._prediction_length = 63 # ~3 months of trading days - self._model = MoiraiForecast( - module=MoiraiModule.from_pretrained( - "Salesforce/moirai-1.0-R-small" - ), - prediction_length=self._prediction_length, - context_length=252, - patch_size="auto", - num_samples=20, - ) - - # Define the universe. - spy = Symbol.create("SPY", SecurityType.EQUITY, Market.USA) - self.universe_settings.schedule.on(self.date_rules.month_start(spy)) - self.universe_settings.resolution = Resolution.DAILY - self._universe = self.add_universe( - self.universe.top( - self.get_parameter('universe_size', 5) - ) - ) - - self._lookback_period = timedelta( - 365 * self.get_parameter('lookback_years', 1) - ) - - # Schedule rebalances. - self._last_rebalance = datetime.min - self.schedule.on( - self.date_rules.month_start(spy, 1), - self.time_rules.midnight, - self._trade - ) - self.set_warmup(timedelta(31)) - - def _sharpe_ratio( - self, weights, returns, risk_free_rate, - trading_days_per_year=252): - mean_returns = returns.mean() * trading_days_per_year - cov_matrix = returns.cov() * trading_days_per_year - portfolio_return = np.sum(mean_returns * weights) - portfolio_std = np.sqrt( - np.dot(weights.T, np.dot(cov_matrix, weights)) - ) - return -(portfolio_return - risk_free_rate) / portfolio_std - - def _optimize_portfolio(self, equity_curves): - returns = equity_curves.pct_change().dropna() - num_assets = returns.shape[1] - result = minimize( - self._sharpe_ratio, - num_assets * [1. / num_assets], - args=( - returns, - self.risk_free_interest_rate_model.get_interest_rate(self.time) - ), - method='SLSQP', - bounds=tuple((0, 1) for _ in range(num_assets)), - constraints=({'type': 'eq', 'fun': lambda w: np.sum(w) - 1}) - ) - return result.x - - def _trade(self): - if self.is_warming_up: - return - if self.time - self._last_rebalance < timedelta(80): - return - self._last_rebalance = self.time - - symbols = list(self._universe.selected) - - # Get historical prices. - history = self.history( - symbols, self._lookback_period - )['close'].unstack(0) - - # Create a GluonTS dataset and generate forecasts. - forecasts = {} - predictor = self._model.create_predictor(batch_size=len(symbols)) - for symbol in symbols: - prices = history[symbol].dropna() - if len(prices) < 60: - continue - df = prices.to_frame('target') - df.index = pd.to_datetime(df.index) - ds = PandasDataset(df, target='target') - for forecast in predictor.predict(ds): - # Take the median over samples. - forecasts[symbol] = np.median(forecast.samples, axis=0) - - if len(forecasts) < 2: - return - - # Build forecasted equity curves and find optimal weights. - forecasts_df = pd.DataFrame(forecasts) - optimal_weights = self._optimize_portfolio(forecasts_df) - - # Rebalance. - tradable_symbols = list(forecasts.keys()) - self.set_holdings( - [ - PortfolioTarget(symbol, optimal_weights[i]) - for i, symbol in enumerate(tradable_symbols) - ], - True - )-This page explains how to use MOMENT in LEAN trading algorithms. The model repository provides the following description:
- --diff --git a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/10 MOMENT/99 Examples.html b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/10 MOMENT/99 Examples.html deleted file mode 100644 index b29d846230..0000000000 --- a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/10 MOMENT/99 Examples.html +++ /dev/null @@ -1,122 +0,0 @@ -- MOMENT is a family of foundation models for general-purpose time series analysis. It supports multiple time series tasks including forecasting, classification, anomaly detection, and imputation out of the box. - MOMENT is pre-trained on a large collection of public time series data called the Time Series Pile, which encompasses diverse domains. - For details, refer to the paper MOMENT: A Family of Open Time-Series Foundation Models. -
-- The following examples demonstrate usage of the MOMENT model. -
-- Example 1: Price Prediction -
-- The following algorithm selects the most liquid assets at the beginning of each month. - Once a quarter, it gets the trailing prices for all the assets in the universe and then forecasts the future price path using MOMENT. - It compares the forecasted price to the current price and goes long assets with a positive forecast and short assets with a negative forecast. -
--diff --git a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/10 MOMENT/metadata.json b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/10 MOMENT/metadata.json deleted file mode 100644 index 6890074947..0000000000 --- a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/10 MOMENT/metadata.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "type": "metadata", - "values": { - "description": "This page explains how to use MOMENT time series forecasting models in LEAN trading algorithms.", - "keywords": "time series forecasting model, MOMENT, pre-trained AI model, price prediction, free AI models", - "og:description": "This page explains how to use MOMENT time series forecasting models in LEAN trading algorithms.", - "og:title": "MOMENT - Documentation QuantConnect.com", - "og:type": "website", - "og:site_name": "MOMENT - QuantConnect.com", - "og:image": "https://cdn.quantconnect.com/docs/i/writing-algorithms/machine-learning/hugging-face/popular-models/moment.png" - } -} diff --git a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/11 Granite TTM/01 Introduction.html b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/11 Granite TTM/01 Introduction.html deleted file mode 100644 index 1038e7f361..0000000000 --- a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/11 Granite TTM/01 Introduction.html +++ /dev/null @@ -1,9 +0,0 @@ -import torch -import numpy as np -from transformers import set_seed -from momentfm import MOMENTPipeline -# endregion - -class MomentAlgorithm(QCAlgorithm): - """ - This algorithm demonstrates how to use the MOMENT time series - foundation model. It forecasts future prices for the 5 most - liquid assets, then allocates the portfolio based on the - expected direction of each asset. - """ - - def initialize(self): - self.set_start_date(2024, 9, 1) - self.set_end_date(2024, 12, 31) - self.set_cash(100_000) - - self.settings.min_absolute_portfolio_target_percentage = 0 - - set_seed(1, True) - - # Load the pre-trained MOMENT model. - self._model = MOMENTPipeline.from_pretrained( - "AutonLab/MOMENT-1-large", - model_kwargs={"task_name": "forecasting", "forecast_horizon": 63} - ) - self._model.init() - - # Define the universe. - spy = Symbol.create("SPY", SecurityType.EQUITY, Market.USA) - self.universe_settings.schedule.on(self.date_rules.month_start(spy)) - self.universe_settings.resolution = Resolution.DAILY - self._universe = self.add_universe( - self.universe.top( - self.get_parameter('universe_size', 5) - ) - ) - - self._lookback_period = timedelta(365) - - # Schedule rebalances. - self._last_rebalance = datetime.min - self.schedule.on( - self.date_rules.month_start(spy, 1), - self.time_rules.midnight, - self._trade - ) - self.set_warmup(timedelta(31)) - - def _trade(self): - if self.is_warming_up: - return - if self.time - self._last_rebalance < timedelta(80): - return - self._last_rebalance = self.time - - symbols = list(self._universe.selected) - - # Get historical prices. - history = self.history( - symbols, self._lookback_period - )['close'].unstack(0) - - signals = {} - for symbol in symbols: - prices = history[symbol].dropna() - if len(prices) < 512: - continue - - # MOMENT expects input of shape (batch, n_channels, seq_len). - context = prices.values[-512:] - input_tensor = torch.tensor( - context - ).float().unsqueeze(0).unsqueeze(0) - - # Generate forecasts. - with torch.no_grad(): - output = self._model(input_tensor) - - forecast = output.forecast.squeeze().numpy() - - # Calculate the expected return from the forecast. - current_price = prices.iloc[-1] - forecasted_end_price = forecast[-1] - expected_return = ( - (forecasted_end_price - current_price) / current_price - ) - signals[symbol] = expected_return - - if not signals: - return - - # Allocate portfolio based on forecast direction. - long_assets = [s for s, v in signals.items() if v > 0] - short_assets = [s for s, v in signals.items() if v <= 0] - - targets = [] - if long_assets: - long_weight = 0.8 / len(long_assets) - for symbol in long_assets: - targets.append(PortfolioTarget(symbol, long_weight)) - if short_assets: - short_weight = -0.2 / len(short_assets) - for symbol in short_assets: - targets.append(PortfolioTarget(symbol, short_weight)) - - self.set_holdings(targets, True)-This page explains how to use Granite TTM in LEAN trading algorithms. The model repository provides the following description:
- --diff --git a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/11 Granite TTM/99 Examples.html b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/11 Granite TTM/99 Examples.html deleted file mode 100644 index c7ffc8dfc0..0000000000 --- a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/11 Granite TTM/99 Examples.html +++ /dev/null @@ -1,132 +0,0 @@ -- Granite-TimeSeries-TTM-R1 (TTM-R1) is a lightweight pre-trained model for time series forecasting. It is based on the decoder-style TSMixer architecture with adaptive patching and resolution prefix tuning. - TTM-R1 achieves state-of-the-art zero-shot forecasting while being significantly smaller than other foundation models for time series. - For details, refer to the paper Tiny Time Mixers (TTMs): Fast Pre-trained Models for Enhanced Zero/Few-Shot Forecasting of Multivariate Time Series. -
-- The following examples demonstrate usage of the Granite TTM model. -
-- Example 1: Price Prediction -
-- The following algorithm selects the most liquid assets at the beginning of each month. - Once a quarter, it gets the trailing prices for all the assets in the universe and then forecasts the future price path using Granite TTM. - It compares the forecasted return to the current price and allocates the portfolio based on forecast direction. -
--diff --git a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/11 Granite TTM/metadata.json b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/11 Granite TTM/metadata.json deleted file mode 100644 index 4a81f10d88..0000000000 --- a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/11 Granite TTM/metadata.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "type": "metadata", - "values": { - "description": "This page explains how to use Granite TTM time series forecasting models in LEAN trading algorithms.", - "keywords": "time series forecasting model, Granite TTM, IBM, pre-trained AI model, price prediction, free AI models", - "og:description": "This page explains how to use Granite TTM time series forecasting models in LEAN trading algorithms.", - "og:title": "Granite TTM - Documentation QuantConnect.com", - "og:type": "website", - "og:site_name": "Granite TTM - QuantConnect.com", - "og:image": "https://cdn.quantconnect.com/docs/i/writing-algorithms/machine-learning/hugging-face/popular-models/granite-ttm.png" - } -} diff --git a/Resources/machine-learning/hugging-face-table.html b/Resources/machine-learning/hugging-face-table.html index 71fef49481..0219505d80 100644 --- a/Resources/machine-learning/hugging-face-table.html +++ b/Resources/machine-learning/hugging-face-table.html @@ -10,7 +10,7 @@import torch -import numpy as np -from transformers import set_seed -from tsfm_public import TinyTimeMixerForPrediction -# endregion - -class GraniteTTMAlgorithm(QCAlgorithm): - """ - This algorithm demonstrates how to use the Granite TTM time series - forecasting model. It forecasts future prices for the 5 most - liquid assets, then allocates the portfolio based on the - expected direction and magnitude of each forecast. - """ - - def initialize(self): - self.set_start_date(2024, 9, 1) - self.set_end_date(2024, 12, 31) - self.set_cash(100_000) - - self.settings.min_absolute_portfolio_target_percentage = 0 - - set_seed(1, True) - - # Load the pre-trained Granite TTM model. - self._model = TinyTimeMixerForPrediction.from_pretrained( - "ibm-granite/granite-timeseries-ttm-r1" - ) - self._model.eval() - - # Define the universe. - spy = Symbol.create("SPY", SecurityType.EQUITY, Market.USA) - self.universe_settings.schedule.on(self.date_rules.month_start(spy)) - self.universe_settings.resolution = Resolution.DAILY - self._universe = self.add_universe( - self.universe.top( - self.get_parameter('universe_size', 5) - ) - ) - - self._lookback_period = timedelta(365) - - # Schedule rebalances. - self._last_rebalance = datetime.min - self.schedule.on( - self.date_rules.month_start(spy, 1), - self.time_rules.midnight, - self._trade - ) - self.set_warmup(timedelta(31)) - - def _trade(self): - if self.is_warming_up: - return - if self.time - self._last_rebalance < timedelta(80): - return - self._last_rebalance = self.time - - symbols = list(self._universe.selected) - - # Get historical prices. - history = self.history( - symbols, self._lookback_period - )['close'].unstack(0) - - signals = {} - for symbol in symbols: - prices = history[symbol].dropna() - if len(prices) < 512: - continue - - # Normalize the price series. - price_values = prices.values[-512:] - mean_price = price_values.mean() - std_price = price_values.std() - if std_price == 0: - continue - normalized = (price_values - mean_price) / std_price - - # TTM expects input of shape (batch, seq_len, n_channels). - input_tensor = torch.tensor( - normalized - ).float().unsqueeze(0).unsqueeze(-1) - - # Generate forecasts. - with torch.no_grad(): - output = self._model(input_tensor) - - # Denormalize the forecast. - forecast = ( - output.prediction_outputs.squeeze().numpy() - * std_price + mean_price - ) - - # Calculate the expected return from the forecast. - current_price = prices.iloc[-1] - forecasted_end_price = float(forecast[-1]) - expected_return = ( - (forecasted_end_price - current_price) / current_price - ) - signals[symbol] = expected_return - - if not signals: - return - - # Allocate portfolio based on forecast direction. - long_assets = [s for s, v in signals.items() if v > 0] - short_assets = [s for s, v in signals.items() if v <= 0] - - targets = [] - if long_assets: - long_weight = 0.8 / len(long_assets) - for symbol in long_assets: - targets.append(PortfolioTarget(symbol, long_weight)) - if short_assets: - short_weight = -0.2 / len(short_assets) - for symbol in short_assets: - targets.append(PortfolioTarget(symbol, short_weight)) - - self.set_holdings(targets, True)-autogluon/chronos-t5-base Time Series Forecasting autogluon/chronos-t5-large Time Series Forecasting - autogluon/chronos-t5-tiny Time Series Forecasting + AutonLab/MOMENT-1-large Time Series Forecasting AutonLab/MOMENT-1-large Time Series Forecasting AventIQ-AI/sentiment-analysis-for-stock-market-sentiment Text Classification bardsai/finance-sentiment-fr-base Text Classification @@ -20,7 +20,7 @@ cardiffnlp/twitter-roberta-base-sentiment-latest Text Classification FacebookAI/roberta-base Fill-Mask google-bert/bert-base-uncased Fill-Mask - google/gemma-7b Text Generation + ibm-granite/granite-timeseries-ttm-r1 Time Series Forecasting ibm-granite/granite-timeseries-ttm-r1 Time Series Forecasting microsoft/deberta-base Fill-Mask mrm8488/distilroberta-finetuned-financial-news-sentiment-analysis Text Classification @@ -28,9 +28,9 @@ nickmuchi/deberta-v3-base-finetuned-finance-text-classification Text Classification nickmuchi/sec-bert-finetuned-finance-classification Text Classification openai-community/gpt2 Text Generation - ProsusAI/finbert Text Classification - Salesforce/moirai-1.0-R-base Time Series Forecasting - Salesforce/moirai-1.0-R-large Time Series Forecasting + Salesforce/moirai-1.0-R-small Time Series Forecasting + Salesforce/moirai-1.0-R-base Time Series Forecasting + Salesforce/moirai-1.0-R-large Time Series Forecasting Salesforce/moirai-1.0-R-small Time Series Forecasting StephanAkkerman/FinTwitBERT-sentiment Text Classification From 314b546c64a2631b09fd755859fe355846188a91 Mon Sep 17 00:00:00 2001 From: Alexandre Catarino yiyanghkust/finbert-tone Text Classification Date: Tue, 17 Mar 2026 22:22:04 +0000 Subject: [PATCH 4/6] Update HuggingFace table generator with new example links (#2102) Add EXAMPLES entries for sentiment-analysis, fill-mask, text-generation, and chronos-bolt pages; regenerate the HTML table. Co-Authored-By: Claude Opus 4.6 --- .../machine-learning/hugging-face-table.html | 89 ++++++++++++++++++- .../hugging-face-table-generator.py | 21 ++++- 2 files changed, 108 insertions(+), 2 deletions(-) diff --git a/Resources/machine-learning/hugging-face-table.html b/Resources/machine-learning/hugging-face-table.html index 0219505d80..9d78d7fe25 100644 --- a/Resources/machine-learning/hugging-face-table.html +++ b/Resources/machine-learning/hugging-face-table.html @@ -11,7 +11,94 @@ autogluon/chronos-t5-large Time Series Forecasting autogluon/chronos-t5-tiny Time Series Forecasting - AutonLab/MOMENT-1-large Time Series Forecasting + AventIQ-AI/sentiment-analysis-for-stock-market-sentiment Text Classification AventIQ-AI/sentiment-analysis-for-stock-market-sentiment type html> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AventIQ-AI/sentiment-analysis-for-stock-market-sentiment · Hugging Face + + + + + + + + + +bardsai/finance-sentiment-fr-base Text Classification cardiffnlp/twitter-roberta-base-sentiment-latest Text Classification diff --git a/code-generators/hugging-face-table-generator.py b/code-generators/hugging-face-table-generator.py index f4d2732b79..e6d0fc6f71 100644 --- a/code-generators/hugging-face-table-generator.py +++ b/code-generators/hugging-face-table-generator.py @@ -34,9 +34,28 @@ Repo: yiyanghkust/finbert-tone. Revisions ['4921590d3c0c3832c0efea24c8381ce0bda7844b']''' EXAMPLES = { + 'chronos-bolt' : '/docs/v2/writing-algorithms/machine-learning/hugging-face/popular-models/chronos-bolt', 'chronos-t5' : '/docs/v2/writing-algorithms/machine-learning/hugging-face/popular-models/chronos-t5', 'distilbert-base-cased-distilled-squad' : '/docs/v2/writing-algorithms/machine-learning/hugging-face/popular-models/distilbert', - 'finbert': '/docs/v2/writing-algorithms/machine-learning/hugging-face/popular-models/finbert' + 'finbert': '/docs/v2/writing-algorithms/machine-learning/hugging-face/popular-models/finbert', + # Sentiment analysis models + 'FinancialBERT-Sentiment-Analysis': '/docs/v2/writing-algorithms/machine-learning/hugging-face/popular-models/sentiment-analysis', + 'sentiment-analysis-for-stock': '/docs/v2/writing-algorithms/machine-learning/hugging-face/popular-models/sentiment-analysis', + 'finance-sentiment-fr': '/docs/v2/writing-algorithms/machine-learning/hugging-face/popular-models/sentiment-analysis', + 'twitter-roberta-base-sentiment': '/docs/v2/writing-algorithms/machine-learning/hugging-face/popular-models/sentiment-analysis', + 'distilroberta-finetuned-financial-news': '/docs/v2/writing-algorithms/machine-learning/hugging-face/popular-models/sentiment-analysis', + 'deberta-v3-base-finetuned-finance': '/docs/v2/writing-algorithms/machine-learning/hugging-face/popular-models/sentiment-analysis', + 'distilroberta-finetuned-financial-text': '/docs/v2/writing-algorithms/machine-learning/hugging-face/popular-models/sentiment-analysis', + 'sec-bert-finetuned': '/docs/v2/writing-algorithms/machine-learning/hugging-face/popular-models/sentiment-analysis', + 'FinTwitBERT': '/docs/v2/writing-algorithms/machine-learning/hugging-face/popular-models/sentiment-analysis', + # Fill-mask models + 'bert-base-uncased': '/docs/v2/writing-algorithms/machine-learning/hugging-face/popular-models/fill-mask', + 'roberta-base': '/docs/v2/writing-algorithms/machine-learning/hugging-face/popular-models/fill-mask', + 'deberta-base': '/docs/v2/writing-algorithms/machine-learning/hugging-face/popular-models/fill-mask', + # Text generation models + 'gpt2': '/docs/v2/writing-algorithms/machine-learning/hugging-face/popular-models/text-generation', + 'gemma-7b': '/docs/v2/writing-algorithms/machine-learning/hugging-face/popular-models/text-generation', + 'DeepSeek-R1-Distill': '/docs/v2/writing-algorithms/machine-learning/hugging-face/popular-models/text-generation', } def __to_row(line): From f417f533a9f543ebde35bbc75407ee41e72c8026 Mon Sep 17 00:00:00 2001 From: Alexandre Catarino deepseek-ai/DeepSeek-R1-Distill-Llama-70B Text Generation Date: Wed, 18 Mar 2026 15:24:08 +0000 Subject: [PATCH 5/6] Mark Sentiment Analysis example as skip-test (backtest >5min) (#2102) The mrm8488/distilroberta placeholder model takes >10 minutes to backtest on QC Cloud due to model size and repeated inference over 4 months of data. Co-Authored-By: Claude Sonnet 4.6 --- .../02 Popular Models/06 Sentiment Analysis/99 Examples.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/06 Sentiment Analysis/99 Examples.html b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/06 Sentiment Analysis/99 Examples.html index 45cbf32b0b..3333aad922 100644 --- a/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/06 Sentiment Analysis/99 Examples.html +++ b/03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/06 Sentiment Analysis/99 Examples.html @@ -12,7 +12,7 @@ If it's negative, it enters a short position. You can replace the model name with any of the sentiment analysis models listed on the introduction page. -
+from transformers import pipeline, set_seed class SentimentAnalysisModelAlgorithm(QCAlgorithm): From ba7530f8314a71edea5c2738cf52081344497ce0 Mon Sep 17 00:00:00 2001 From: Alexandre CatarinoDate: Wed, 18 Mar 2026 17:22:16 +0000 Subject: [PATCH 6/6] Update run_model_variants: timeout 1800s, reuse project, skip-test logic (#2102) - Increase BACKTEST_TIMEOUT to 1800s to handle large models - Add REUSE_PROJECT_ID to avoid daily project creation limit - Auto-mark example as skip-test when placeholder backtest exceeds 5 min - Add SKIP_CATEGORIES to resume partial runs - Add traceback output for better error diagnosis Co-Authored-By: Claude Sonnet 4.6 --- examples-check/model_variant_results.json | 66 ++++ examples-check/run_model_variants.py | 376 ++++++++++++++++++++++ 2 files changed, 442 insertions(+) create mode 100644 examples-check/model_variant_results.json create mode 100644 examples-check/run_model_variants.py diff --git a/examples-check/model_variant_results.json b/examples-check/model_variant_results.json new file mode 100644 index 0000000000..c0486f36a3 --- /dev/null +++ b/examples-check/model_variant_results.json @@ -0,0 +1,66 @@ +[ + [ + "Fill-Mask", + "distilbert/distilbert-base-uncased", + { + "net_profit": "29.430%", + "drawdown": "29.000%", + "sharpe": "1.586" + } + ], + [ + "Fill-Mask", + "FacebookAI/roberta-base", + { + "net_profit": "-9.417%", + "drawdown": "16.700%", + "sharpe": "-1.296" + } + ], + [ + "Fill-Mask", + "google-bert/bert-base-uncased", + { + "net_profit": "-9.417%", + "drawdown": "16.700%", + "sharpe": "-1.296" + } + ], + [ + "Fill-Mask", + "microsoft/deberta-base", + "FAIL: backtest timed out" + ], + [ + "Text Generation", + "openai-community/gpt2", + { + "net_profit": "19.558%", + "drawdown": "9.900%", + "sharpe": "1.952" + } + ], + [ + "Text Generation", + "google/gemma-7b", + "FAIL: backtest timed out" + ], + [ + "Text Generation", + "deepseek-ai/DeepSeek-R1-Distill-Llama-70B", + { + "net_profit": "N/A", + "drawdown": "N/A", + "sharpe": "N/A" + } + ], + [ + "Chronos-Bolt", + "autogluon/chronos-bolt-base", + { + "net_profit": "7.246%", + "drawdown": "6.100%", + "sharpe": "0.686" + } + ] +] \ No newline at end of file diff --git a/examples-check/run_model_variants.py b/examples-check/run_model_variants.py new file mode 100644 index 0000000000..09da7bd9eb --- /dev/null +++ b/examples-check/run_model_variants.py @@ -0,0 +1,376 @@ +"""Run backtests for all HuggingFace model variants and output a results table. + +Usage: + python examples-check/run_model_variants.py + +Credentials (checked in order): + 1. Environment variables: DOCS_REGRESSION_TEST_USER_ID, DOCS_REGRESSION_TEST_USER_TOKEN + 2. LEAN CLI credentials file: ~/.lean/credentials +""" +import sys +import os +import json +import time +import base64 +import hashlib +from pathlib import Path + +try: + import requests +except ImportError: + sys.exit("Missing dependency: pip install requests") + +try: + from bs4 import BeautifulSoup +except ImportError: + sys.exit("Missing dependency: pip install beautifulsoup4") + + +# --------------------------------------------------------------------------- +# Credentials & API helpers +# --------------------------------------------------------------------------- + +def _load_credentials(): + user_id = os.environ.get("DOCS_REGRESSION_TEST_USER_ID", "") + user_token = os.environ.get("DOCS_REGRESSION_TEST_USER_TOKEN", "") + if user_id and user_token: + return user_id, user_token + lean_creds = Path.home() / ".lean" / "credentials" + if lean_creds.exists(): + try: + creds = json.loads(lean_creds.read_text()) + user_id = str(creds.get("user-id", "")) + user_token = creds.get("api-token", "") + if user_id and user_token: + return user_id, user_token + except (json.JSONDecodeError, KeyError): + pass + return "", "" + + +BASE_API = "https://www.quantconnect.com/api/v2" +USER_ID, USER_TOKEN = _load_credentials() +PYTHON_IMPORTS = "from AlgorithmImports import *\n" +MAX_RETRIES = 5 +POLL_INTERVAL = 10 # seconds between status polls +BACKTEST_TIMEOUT = 1800 # seconds +SLOW_THRESHOLD = 300 # seconds; placeholder backtests over this become skip-test +# Set to a list of category names to skip (e.g. already tested ones) +SKIP_CATEGORIES = ["Sentiment Analysis"] +# Reuse an existing project instead of creating new ones (avoids 100/day limit). +# Set to an integer project ID, or None to always create fresh projects. +REUSE_PROJECT_ID = 29125823 + + +def _headers(): + ts = str(int(time.time())) + hashed = hashlib.sha256(f"{USER_TOKEN}:{ts}".encode()).hexdigest() + auth = base64.b64encode(f"{USER_ID}:{hashed}".encode()).decode() + return {"Authorization": f"Basic {auth}", "Timestamp": ts} + + +def api_post(endpoint, payload=None): + return requests.post( + f"{BASE_API}/{endpoint}", + headers=_headers(), + json=payload or {} + ).json() + + +def clean_code(code): + return (code + .replace("&&", "&&") + .replace("<", "<") + .replace(">", ">")) + + +# --------------------------------------------------------------------------- +# HTML helpers +# --------------------------------------------------------------------------- + +def mark_skip_test(file_path): + """Change 'testable' to 'skip-test' on the first matching example div.""" + content = Path(file_path).read_text(encoding="utf-8") + updated = content.replace( + 'class="section-example-container testable"', + 'class="section-example-container skip-test"', + 1 # only first occurrence + ) + if updated != content: + Path(file_path).write_text(updated, encoding="utf-8") + print(f" → Backtest exceeded {SLOW_THRESHOLD}s — marked skip-test: {Path(file_path).name}") + + +# --------------------------------------------------------------------------- +# Extract base code from HTML example file +# --------------------------------------------------------------------------- + +def extract_python_code(file_path): + """Return the first testable Python QCAlgorithm code block in the file.""" + html = Path(file_path).read_text(encoding="utf-8") + soup = BeautifulSoup(html, "html.parser") + for div in soup.find_all("div", class_="section-example-container"): + classes = div.get("class", []) + if "skip-test" in classes: + continue + for pre in div.find_all("pre", class_="python"): + code = pre.get_text() + if "(QCAlgorithm)" in code: + return clean_code(code) + raise ValueError(f"No testable Python QCAlgorithm block found in {file_path}") + + +# --------------------------------------------------------------------------- +# QC Cloud: create project, compile, backtest +# --------------------------------------------------------------------------- + +def create_project_and_upload(code, label): + if REUSE_PROJECT_ID: + project_id = REUSE_PROJECT_ID + else: + ts = int(time.time()) + name = f"DocTest/variants_{ts}_{label[:20]}" + resp = api_post("projects/create", {"name": name, "language": "Py"}) + if not (resp.get("success") or resp.get("projects")): + raise RuntimeError(f"project create failed: {resp}") + project_id = resp["projects"][0]["projectId"] + + full_code = PYTHON_IMPORTS + code + for _ in range(MAX_RETRIES): + r = api_post("files/update", { + "projectId": project_id, + "name": "main.py", + "content": full_code + }) + if r.get("success"): + break + time.sleep(3) + else: + raise RuntimeError("file upload failed") + + return project_id + + +def compile_project(project_id): + resp = api_post("compile/create", {"projectId": project_id}) + if not resp.get("success"): + raise RuntimeError(f"compile create failed: {resp}") + compile_id = resp.get("compileId") or resp.get("compile", {}).get("compileId") + if not compile_id: + raise RuntimeError(f"no compileId in response: {resp}") + + deadline = time.time() + 300 + while time.time() < deadline: + time.sleep(POLL_INTERVAL) + r = api_post("compile/read", {"projectId": project_id, "compileId": compile_id}) + state = r.get("state") or r.get("compile", {}).get("state", "") + if state == "BuildSuccess": + return compile_id + if state == "BuildError": + logs = r.get("logs") or r.get("compile", {}).get("logs", []) + raise RuntimeError(f"compile error: {logs[-3:] if logs else r}") + raise RuntimeError("compile timed out") + + +def run_backtest(project_id, compile_id, label): + resp = api_post("backtests/create", { + "projectId": project_id, + "compileId": compile_id, + "backtestName": label[:64] + }) + if not resp.get("success"): + raise RuntimeError(f"backtest create failed: {resp}") + bt_id = resp["backtest"]["backtestId"] + + deadline = time.time() + BACKTEST_TIMEOUT + while time.time() < deadline: + time.sleep(POLL_INTERVAL) + r = api_post("backtests/read", {"projectId": project_id, "backtestId": bt_id}) + bt = r.get("backtest", {}) + progress = bt.get("progress", 0) + completed = bt.get("completed", False) + if completed: + return bt + if bt.get("error"): + raise RuntimeError(f"backtest error: {bt['error']}") + print(f" ... {int(progress*100)}% complete") + raise RuntimeError("backtest timed out") + + +def extract_stats(bt): + # 'statistics' has the full set of metrics we need + stats = bt.get("statistics") or {} + + def get(key, fallback="N/A"): + val = stats.get(key, fallback) + return val if val not in ("", None) else fallback + + return { + "net_profit": get("Net Profit"), + "drawdown": get("Drawdown"), + "sharpe": get("Sharpe Ratio"), + } + + +# --------------------------------------------------------------------------- +# Model variants per category +# --------------------------------------------------------------------------- + +BASE = Path(__file__).parent.parent + +CATEGORIES = [ + { + "name": "Sentiment Analysis", + "file": BASE / "03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/06 Sentiment Analysis/99 Examples.html", + "placeholder": "mrm8488/distilroberta-finetuned-financial-news-sentiment-analysis", + "models": [ + "mrm8488/distilroberta-finetuned-financial-news-sentiment-analysis", + "ahmedrachid/FinancialBERT-Sentiment-Analysis", + "AventIQ-AI/sentiment-analysis-for-stock-market-sentiment", + "bardsai/finance-sentiment-fr-base", + "cardiffnlp/twitter-roberta-base-sentiment-latest", + "nickmuchi/deberta-v3-base-finetuned-finance-text-classification", + "nickmuchi/distilroberta-finetuned-financial-text-classification", + "nickmuchi/sec-bert-finetuned-finance-classification", + "StephanAkkerman/FinTwitBERT-sentiment", + ], + }, + { + "name": "Fill-Mask", + "file": BASE / "03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/07 Fill-Mask/99 Examples.html", + "placeholder": "distilbert/distilbert-base-uncased", + "models": [ + "distilbert/distilbert-base-uncased", + "FacebookAI/roberta-base", + "google-bert/bert-base-uncased", + "microsoft/deberta-base", + ], + }, + { + "name": "Text Generation", + "file": BASE / "03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/08 Text Generation/99 Examples.html", + "placeholder": "openai-community/gpt2", + "models": [ + "openai-community/gpt2", + "google/gemma-7b", + "deepseek-ai/DeepSeek-R1-Distill-Llama-70B", + ], + }, + { + "name": "Chronos-Bolt", + "file": BASE / "03 Writing Algorithms/31 Machine Learning/04 Hugging Face/02 Popular Models/12 Chronos-Bolt/99 Examples.html", + "placeholder": "autogluon/chronos-bolt-base", + "models": [ + "autogluon/chronos-bolt-base", + ], + }, +] + + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- + +def main(): + if not USER_ID or not USER_TOKEN: + sys.exit( + "Error: Set DOCS_REGRESSION_TEST_USER_ID and " + "DOCS_REGRESSION_TEST_USER_TOKEN environment variables." + ) + + resp = api_post("authenticate") + if not resp.get("success"): + sys.exit("API authentication failed. Check your credentials.") + print("API authentication successful.\n") + + results = [] # list of (category, model, stats_dict | error_str) + + for cat in CATEGORIES: + if cat["name"] in SKIP_CATEGORIES: + print(f"\nSkipping: {cat['name']}") + continue + print(f"\n{'='*60}") + print(f"Category: {cat['name']}") + print(f"{'='*60}") + + try: + base_code = extract_python_code(str(cat["file"])) + except Exception as e: + print(f" ERROR reading code: {e}") + for model in cat["models"]: + results.append((cat["name"], model, f"code read error: {e}")) + continue + + for model in cat["models"]: + label = f"{cat['name'][:8]}_{model.split('/')[-1][:20]}" + print(f"\n Model: {model}") + try: + code = base_code.replace(cat["placeholder"], model) + + print(f" Creating project...") + project_id = create_project_and_upload(code, label) + + print(f" Compiling (project {project_id})...") + compile_id = compile_project(project_id) + + print(f" Running backtest...") + bt_start = time.time() + bt = run_backtest(project_id, compile_id, label) + bt_elapsed = time.time() - bt_start + + if model == cat["placeholder"] and bt_elapsed > SLOW_THRESHOLD: + mark_skip_test(str(cat["file"])) + + stats = extract_stats(bt) + print(f" PASS — net profit={stats['net_profit']} " + f"drawdown={stats['drawdown']} sharpe={stats['sharpe']} " + f"({int(bt_elapsed)}s)") + results.append((cat["name"], model, stats)) + + except Exception as e: + import traceback + traceback.print_exc() + print(f" FAIL — {e}") + results.append((cat["name"], model, f"FAIL: {e}")) + + # ----------------------------------------------------------------------- + # Print results table + # ----------------------------------------------------------------------- + print("\n\n" + "="*80) + print("RESULTS TABLE (Markdown)") + print("="*80) + print() + + # Group by category + categories_seen = [] + rows_by_cat = {} + for cat_name, model, stats in results: + if cat_name not in rows_by_cat: + rows_by_cat[cat_name] = [] + categories_seen.append(cat_name) + rows_by_cat[cat_name].append((model, stats)) + + for cat_name in categories_seen: + rows = rows_by_cat[cat_name] + print(f"### {cat_name}\n") + print("| Model | Net Profit | Drawdown | Sharpe Ratio |") + print("|-------|-----------|----------|--------------|") + for model, stats in rows: + if isinstance(stats, dict): + np_ = stats['net_profit'] + dd = stats['drawdown'] + sr = stats['sharpe'] + print(f"| `{model}` | {np_} | {dd} | {sr} |") + else: + print(f"| `{model}` | {stats} | — | — |") + print() + + # Also dump raw JSON for debugging + output_path = Path(__file__).parent / "model_variant_results.json" + with open(output_path, "w") as f: + json.dump(results, f, indent=2, default=str) + print(f"Raw results saved to {output_path}") + + +if __name__ == "__main__": + main()