---
title: "Insight Manager"
description: "The InsightManager tracks all of the Insight objects by storing insights in an internal dictionary which is accessible anywhere in the algorithm."
canonical: https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/insight-manager
markdown: https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/insight-manager.md
language: python
source: https://github.com/QuantConnect/Documentation/tree/master/03%20Writing%20Algorithms/34%20Algorithm%20Framework/08%20Insight%20Manager
modified: 2026-10-30
index: https://www.quantconnect.com/llms.txt
---

# Insight Manager

[Documentation](https://www.quantconnect.com/docs/v2.md) > [Writing Algorithms](https://www.quantconnect.com/docs/v2/writing-algorithms.md) > Algorithm Framework > Insight Manager

> Code examples are shown in Python. Append `?lang=csharp` to this URL for the C# version of this page.

## Introduction

The `InsightManager` tracks all of the [Insight objects](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/alpha/key-concepts.md#06-Insights) in your algorithm. It's an `InsightCollection`, which stores all of your insights in an internal dictionary that has the security `Symbol` as the key and a list of `Insight` objects as the value. You can access the manager anywhere in your algorithm where you have reference to the algorithm class. If you want to use an `InsightCollection` in your algorithm that's seperate from the `InsightManager`, create a new `InsightCollection`.

## Check Membership

To check if an insight exists in the `InsightManager`, call the `contains` method.

```python
# Check the algorithms central insight collection (InsightManager) to avoid emitting duplicates, reducing the processing work.
if algorithm.insights.contains(insight):
    pass
```

To check if the `InsightManager` has an insight for a `Symbol`, call the `contains_key` method.

```python
# Check the algorithms central insight collection (InsightManager) if there's already been an insight for this symbol across the other alphas.
if algorithm.insights.contains_key(symbol):
    pass
```

To check if the `InsightManager` has active insights for a `Symbol` at a specific Coordinated Universal Time (UTC), call the `has_active_insights` method.

```python
# Check if there are active insights for a specific symbol at the given time to determine if alpha should emit more insights
if algorithm.insights.has_active_insights(symbol, utc_time):
    pass
```

## Get Insights

To get the insights for a `Symbol`, index the `InsightManager` with the `Symbol`.

```python
if algorithm.insights.contains_key(symbol):
    insights = algorithm.insights[symbol]
```

To get the insights that pass a filter, call the `get_insights` method.

```python
insights = algorithm.insights.get_insights(lambda insight: insight.direction == Insightdirection.up)
```

To iterate through the `InsightManager`, call the `get_enumerator` method.

```python
enumerator = algorithm.insights.get_enumerator()
```

To get all of the insights that will be active at a certain UTC time, call the `get_active_insights` method.

```python
active_insights = algorithm.insights.get_active_insights(utc_time)
```

## Get the Next Expiry Time

To get the next insight expiry time in UTC, call the `get_next_expiry_time` method.

```python
next_expiry = algorithm.insights.get_next_expiry_time()
```

## Remove Insights

Only the [Portfolio Construction model](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/portfolio-construction/key-concepts.md) should remove insights from the `InsightManager`. It should remove insights when the insights expire and when the corresponding security leaves the [universe](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/universe-selection/key-concepts.md).

To remove an insight from the `InsightManager`, call the `remove` method.

```python
remove_successful = algorithm.insights.remove(insight)
```

To remove all the insights for a set of `Symbol` objects, pass a list of `Symbol` objects the `clear` method.

```python
algorithm.insights.clear(symbols)
```

To remove all the insights that will be expired at a certain UTC time, call the `remove_expired_insights` method.

```python
expired_insights = algorithm.insights.remove_expired_insights(utc_time)
```

## Cancel Insights

In some cases, you may want to cancel an Insight. For example, if a [Risk Management model](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/risk-management/key-concepts.md) in your algorithm liquidates a security, it should also cancel all of the active insights for the security. If you don't cancel the insights, the [Portfolio Construction model](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/portfolio-construction/key-concepts.md) might create a new [PortfolioTarget](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/portfolio-construction/key-concepts.md#05-Portfolio-Targets) to re-enter the position.

Another example of a situtation where you would want to cancel an Insight is when an [Alpha model](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/alpha/key-concepts.md) in your algorithm determines the trading opportunity has pre-maturely ended. For instance, say you want your algorithm to enter a long position in a security when its [Relative Strength Index](https://www.quantconnect.com/docs/v2/writing-algorithms/indicators/supported-indicators/relative-strength-index.md) (RSI) moves below 20 and then liquidate the position when the security's RSI moves above 30. If you emit an Insight that has a duration of 30 days when the RSI moves below 20 but the RSI moves above 30 in 10 days, you should cancel the Insight when the RSI moves above 30.

To cancel insights, call the `cancel`/`expire` method with a list of `Insight` objects.

```python
algorithm.insights.cancel(insights)
```

To cancel all the insights for some securities, call the `cancel`/`expire` method with a list of `Symbol` objects.

```python
algorithm.insights.cancel(symbols)
```

When you cancel an active insight, it's `close_time_utc` property is set to one second into the past.

## Add Insights to Self-Managed Insight Collections

To add insights to the `InsightManager`, return a list of `Insight` objects from the [update method](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/alpha/key-concepts.md#04-Model-Structure) of your Alpha model or call the `emit_insights` method. If you manage an `InsightCollection` that's seperate from the `InsightManager`, there are some methods to add `Insight` objects to it.

To add an insight to an `InsightCollection`, call the `add` method.

```python
# You can manually add an Insight to an InsightCollection object to organize, fetch, or cancel insights an alpha has generated.
self.insight_collection.add(insight)
```

To add a list of insights, call the `add_range` method.

```python
# You can manually add a list of Insights to an InsightCollection object to organize, fetch, or cancel insights an alpha has generated.
self.insight_collection.add_range(insights)
```

## Preserve Insights Between Deployments

Follow these steps to use the Object Store to preserve the algorithm state across live deployments:

1. Create an algorithm that defines a storage key and adds insights to the [Insight Manager](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/insight-manager.md).
   ```python
   class ObjectStoreChartingAlgorithm(QCAlgorithm):
       def initialize(self):
           self.insight_key = f"{self.project_id}/insights"
           self.set_universe_selection(ManualUniverseSelectionModel([ Symbol.create("SPY", SecurityType.EQUITY, Market.USA) ]))
           self.set_alpha(ConstantAlphaModel(InsightType.PRICE, InsightDirection.UP, timedelta(5), 0.025, None))
   ```
2. At the top of the algorithm file, add the following imports:
   ```python
   from Newtonsoft.Json import JsonConvert
   from System.Collections.Generic import List
   ```
   `Insight` objects are C# objects, so you need the preceding C# libraries to serialize and deserialize them.
3. In the [on_end_of_algorithm](https://www.quantconnect.com/docs/v2/writing-algorithms/key-concepts/event-handlers.md#15-End-Of-Algorithm-Events) event handler of the algorithm, [get the Insight objects](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/insight-manager.md#04-Get-Insights) and save them in the Object Store as a JSON object.
   ```python
   def on_end_of_algorithm(self):
       insights = self.insights.get_insights(lambda x: x.is_active(self.utc_time))
       content = ','.join([JsonConvert.SerializeObject(x) for x in insights])
       self.object_store.save(self.insight_key, f'[{content}]')
   ```
4. At the bottom of the `initialize` method, read the Insight objects from the Object Store and [add them to the Insight Manager](https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/insight-manager.md#11-Add-Insights-to-Self-Managed-Insight-Collections).
   ```python
   if self.object_store.contains_key(self.insight_key):
       insights = JsonConvert.DeserializeObject[List[Insight]](
           self.object_store.read(self.insights_key)
       )
       self.insights.add_range(insights)
   ```

The following algorithm provides a full example of preserving the Insight state between deployments:

```python
from Newtonsoft.Json import JsonConvert
from System.Collections.Generic import List

class ObjectStoreInsightsAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.universe_settings.resolution = Resolution.DAILY

        self.set_start_date(2024, 9, 1)
        self.set_end_date(2024, 12, 31)
        self.set_cash(100000)

        self.insights_key = f"{self.project_id}/insights"

        # Read the file with the insights
        if self.object_store.contains_key(self.insights_key):
            # Load cached JSON key value pair from object store, then add to algorithm insights.
            insights = JsonConvert.DeserializeObject[List[Insight]](
                self.object_store.read(self.insights_key)
            )
            self.log(f"Read {len(insights)} insight(s) from the Object Store")
            self.insights.add_range(insights)

            # Delete the key to reuse it
            self.object_store.delete(self.insights_key)

        self.set_universe_selection(ManualUniverseSelectionModel([ Symbol.create("SPY", SecurityType.EQUITY, Market.USA) ]))
        self.set_alpha(ConstantAlphaModel(InsightType.PRICE, InsightDirection.UP, timedelta(5), 0.025, None))
        self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel(Resolution.DAILY))

    def on_end_of_algorithm(self) -> None:
        # Get all active insights.
        insights = self.insights.get_insights(lambda x: x.is_active(self.utc_time))
        # If we want to save all insights (expired and active), we can use
        # insights = self.insights.get_insights(lambda x: True)
        self.log(f"Save {len(insights)} insight(s) to the Object Store.")
        content = ','.join([JsonConvert.SerializeObject(x) for x in insights])
        self.object_store.save(self.insights_key, f'[{content}]')
```

## Properties

The `InsightManager` has the following properties:

*Members of `QuantConnect.Algorithm.Framework.Alphas.Analysis.InsightManager` (JSON): https://www.quantconnect.com/services/inspector?type=T:QuantConnect.Algorithm.Framework.Alphas.Analysis.InsightManager&language=python*

## Examples

The following examples demonstrate some common practices for using insight manager.

### Example 1: Cancel Insight

If the risk management model only submits the `PortfolioTarget` to liquidate the security, it will be re-ordered immediately due to the active insight. Hence, we also need to add logic to cancel the insight in the `manage_risk` method of the risk management model. The below example only permits 1-day holding time per each insight through a custom risk management model.

```python
class FrameworkInsightManagerAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        self.set_start_date(2024, 9, 1)
        self.set_end_date(2024, 11, 15)
        self.set_cash(1000000)
        # Seed the price of each asset with its last known price to avoid trading errors.
        self.settings.seed_initial_prices = True
        # Add a universe of the most liquid stocks since their trend is more capital-supported.
        self.add_universe_selection(QC500UniverseSelectionModel())
        # Emit insights on EMA cross, indicating the trend changes. We use short-term versus medium-term for more trade opportunities.
        self.add_alpha(EmaCrossAlphaModel(20, 60, Resolution.DAILY))
        # Equal weighting on each insight to dissipate capital risk evenly.
        self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())

        # Add a risk management model that controls maximum holding time.
        self.add_risk_management(MaximumHoldingTimeRiskManagementModel(timedelta(5)))

class MaximumHoldingTimeRiskManagementModel(RiskManagementModel):
    def __init__(self, maximum_holding_time: timedelta) -> None:
        self.maximum_holding_time = maximum_holding_time
        # A dictionary to hold the holding time of each position.
        self.holding_time = {}

    def manage_risk(self, algorithm: QCAlgorithm, targets: List[PortfolioTarget]) -> List[PortfolioTarget]:
        targets = []

        for symbol, holding in algorithm.portfolio.items():
            if holding.invested:
                if symbol not in self.holding_time:
                    # For newly open positions, save the entry time.
                    self.holding_time[symbol] = algorithm.time
                elif algorithm.time - self.holding_time[symbol] > self.maximum_holding_time:
                    # Liquidate the position if the holding time exceeds the permitted level.
                    targets.append(PortfolioTarget(symbol, 0))
                    # Cancel the insight to avoid repeated re-ordering.
                    algorithm.insights.cancel([symbol])
                    # Remove from the dictionary.
                    self.holding_time.pop(symbol)

        return targets
```
