investfly.models.strategy.TradingStrategy

Trading strategy base class and interface.

This module defines the TradingStrategy abstract base class that all trading strategies must inherit from. It provides the public callbacks and runtime-injected services used by market-data-driven and time-scheduled strategies.

class TradingStrategy(abc.ABC):

Base class for all trading strategies.

TradingStrategy is an abstract base class that defines the interface and common functionality for all trading strategies. Strategies can be triggered by market data events (bars/ticks) or scheduled time intervals.

Key Features

  • Configuration: Strategies can accept configuration parameters via the config dictionary in the constructor.
  • State Management: Strategies can maintain persistent state across executions by using self.state directly. The execution engine automatically persists and restores self.state between executions.
  • Stateless Execution: Each callback is executed on a fresh instance, ensuring thread-safety and isolation.
  • Data Access: Access to market data and indicators via dataService.
  • Context Access: Access to portfolio and universe securities via context.

Required Implementation

Subclasses must implement:

Optional Implementation

Subclasses may override:

  • onMarketData(): Handle market data updates (requires @data_trigger decorator).
  • Scheduled methods: Handle wall-clock events. Decorate one or more methods with @scheduled(TriggerSchedule...).
  • getStrategyPolicy(): Define runtime-managed exits, scaling, lifecycle, and limits.

    Attributes

  • config (Dict[str, Any] | None): Strategy-specific configuration parameters.

  • state (StrategyState): Persistent state dictionary for the strategy. Initialized as an empty dictionary. Child classes can use this directly to store state values. The execution engine automatically persists and restores this dictionary between executions, so child classes just need to read/write to self.state directly.
  • dataService (StrategyDataService): Service for accessing market data and indicators.
  • services (StrategyServices): Runtime-managed planning, selection, and evaluation helpers.
  • context (StrategyExecutionContext): Execution context containing portfolio and universe.

Example

A minimal runtime-planned allocation:

class AllocationStrategy(TradingStrategy):
    def getSecurityUniverseSelector(self):
        return SecurityUniverseSelector.fromSymbols(SecurityType.STOCK, ["AAPL", "MSFT"])

    @data_trigger(type=DataType.BARS, barInterval=BarInterval.ONE_DAY)
    def onMarketData(self, updatedSecurities):
        execution = OpenExecutionSettings(
            PositionSizeSpec(PositionSizeMode.PERCENT_OF_PORTFOLIO, 80.0),
            OrderSpec(),
            DirectInstrumentSelection(),
        )
        return self.services.planAllocationOrders(AllocationPlan(updatedSecurities, execution))
TradingStrategy(config: Optional[Dict[str, Any]] = None)

Initialize strategy with optional configuration.

Args: config: Dictionary of strategy-specific parameters. Can contain any structure (numeric, list, string, nested dictionaries, etc.). Defaults to None if no configuration is provided.

Note: The dataService and context attributes are set by the execution engine before strategy methods are called. They should not be set manually in the constructor.

config
state: Dict[str, Union[int, float, bool, str, List[Union[int, float, bool, str]], Dict[str, Any], NoneType]]
def setDataService( self, dataService: investfly.models.strategy.StrategyDataService) -> None:

Set the data service for accessing market data and indicators.

This method is called by the execution engine to inject the data service into the strategy instance. The data service provides access to:

  • Indicator computation (SMA, RSI, MACD, etc.)
  • Market data (quotes, bars, financials, news)

Args: dataService: The strategy data service instance. This is a singleton service shared across all strategy executions.

Note: This method is typically called automatically by the execution engine. Strategies should not call this method directly.

def setContext( self, context: investfly.models.strategy.StrategyExecutionContext) -> None:

Set the execution context for the strategy instance.

This method is called by the execution engine to inject the execution context into the strategy instance. The context contains:

  • Portfolio: Current portfolio state with positions and balances
  • Universe securities: List of securities in the strategy's trading universe

Args: context: The execution context containing portfolio and universe data.

Note: This method is typically called automatically by the execution engine. Strategies should not call this method directly.

def setServices( self, services: investfly.models.strategy.StrategyServices) -> None:

Inject the runtime implementation of the public strategy helper contract.

def getPortfolio(self) -> investfly.models.portfolio.Portfolio:

Get the portfolio from the execution context.

Returns: The current portfolio state containing: - Open positions - Account balances - Portfolio performance metrics

Example:

portfolio = self.getPortfolio()
portfolio_value = portfolio.balances.currentValue
for position in portfolio.openPositions:
    print(f"{position.security.symbol}: {position.quantity} shares")
def getUniverseSecurities(self) -> List[investfly.models.marketdata.Security]:

Get the universe securities from the execution context.

Returns: List of Security objects representing all securities in the strategy's trading universe. This is the resolved list based on the security universe selector returned by getSecurityUniverseSelector().

Example:

universe = self.getUniverseSecurities()
for security in universe:
    quote = self.dataService.getQuote(security)
    if quote:
        print(f"{security.symbol}: {quote.lastPrice}")
def getStateValue( self, key: str) -> Union[int, float, bool, str, List[Union[int, float, bool, str]], Dict[str, Any], NoneType]:

Get the state value for the given key.

Args: key: The key to get the state value for.

Returns: The state value for the given key.

def getState( self, key: str, default: Union[int, float, bool, str, List[Union[int, float, bool, str]], Dict[str, Any], NoneType] = None) -> Union[int, float, bool, str, List[Union[int, float, bool, str]], Dict[str, Any], NoneType]:
def setState( self, key: str, value: Union[int, float, bool, str, List[Union[int, float, bool, str]], Dict[str, Any], NoneType]) -> None:
def deleteState(self, key: str) -> None:
@abstractmethod
def getSecurityUniverseSelector( self) -> investfly.models.strategy.SecurityUniverseSelector:

Return the security universe selector for this strategy.

This method must be implemented by all strategy subclasses. It defines which securities the strategy will evaluate and trade.

Returns: A SecurityUniverseSelector object that defines the strategy's trading universe. The selector can specify: - A single security - A standard predefined list (e.g., S&P 500, NASDAQ 100) - A custom list of symbols - A fundamental query-based selection

Example:

# Single stock
return SecurityUniverseSelector.singleStock("AAPL")

# Standard list
return SecurityUniverseSelector.fromStandardList(StandardSymbolsList.SP_500)

# Custom list
return SecurityUniverseSelector.fromSymbols(
    SecurityType.STOCK, ["AAPL", "MSFT", "GOOGL"]
)

# Fundamental query
query = FinancialQuery(
    SecurityType.STOCK,
    FinancialCondition(FinancialField.MARKET_CAP, ComparisonOperator.GREATER_THAN, 1000000000)
)
return SecurityUniverseSelector.fromFinancialQuery(query)
def onMarketData( self, updatedSecurities: List[investfly.models.marketdata.Security]) -> Optional[List[investfly.models.portfolio.TradeOrder]]:

Handle market data updates (bars/ticks).

This optional method is called whenever market data is updated for securities in the strategy's universe. To enable this callback, override this method and decorate it with @data_trigger.

The @data_trigger decorator specifies when this method should be called:

  • type (DataType): The type of market data to trigger on. Currently supported: DataType.BARS (bar/candlestick data).
  • barInterval (BarInterval, optional): Required for BARS type. Options: ONE_MINUTE, FIVE_MINUTE, FIFTEEN_MINUTE, THIRTY_MINUTE, SIXTY_MINUTE, ONE_DAY.

Args: updatedSecurities: List of Security objects that received market data updates triggering this callback. Strategies should iterate over this list instead of the full universe to process only securities with new data.

Returns: Optional list of TradeOrder objects to execute. Return None or an empty list if no trades should be placed.

Note: - If your logic requires Quote data (e.g., LastPrice), use @data_trigger(type=DataType.BARS, barInterval=BarInterval.ONE_MINUTE) to trigger on every 1-minute bar update, then access quotes using self.dataService.getQuote(security). - The method is called only for securities that have received updates, not for the entire universe.

Example:

@data_trigger(type=DataType.BARS, barInterval=BarInterval.ONE_DAY)
def onMarketData(self, updatedSecurities):
    orders = []
    for security in updatedSecurities:
        # Compute indicators
        sma = self.dataService.computeIndicatorSeries(
            "SMA", security, {"period": 20, "barInterval": BarInterval.ONE_DAY}
        )
        # Generate trade orders based on indicator values
        if sma.last.value > threshold:
            orders.append(TradeOrder(security, TradeType.BUY, OrderType.MARKET_ORDER))
    return orders if orders else None
def onSchedule( self, event: investfly.models.strategy.ScheduleEvent) -> Optional[List[investfly.models.portfolio.TradeOrder]]:

Handle scheduled time-based events.

Custom strategies may decorate any number of scheduled methods with @scheduled(TriggerSchedule...). The execution engine discovers all decorated callbacks through getScheduledCallbacks() and passes a ScheduleEvent to each callback.

Returns: Optional list of TradeOrder objects to execute. Return None or an empty list if no trades should be placed.

Note: - Multiple methods can be decorated with @scheduled to run distinct jobs at distinct schedule patterns. - The event argument includes the callback name, the schedule, the scheduled time, the actual evaluation time, and whether the callback is executing inside a backtest.

Example:

@scheduled(TriggerSchedule.daily("10:00"))
def rebalanceDaily(self, event: ScheduleEvent):
    orders = []
    portfolio = self.getPortfolio()
    return orders

@scheduled(TriggerSchedule.interval(5, days=ScheduleDayMode.EVERYDAY, window=ScheduleWindow("09:00", "13:00")))
def checkPositions(self, event: ScheduleEvent):
    return None
def getScheduledCallbacks( self) -> List[investfly.models.strategy.ScheduledCallback]:

Return all scheduled callbacks exposed by this strategy instance.

def getScheduledCallback( self, callbackName: str) -> investfly.models.strategy.ScheduledCallback:
def getStrategyPolicy( self) -> investfly.models.strategy.CustomStrategyPolicy | None:

Return runtime-managed position, lifecycle, and portfolio behavior.

The same contract models used by configuration-driven strategies are accepted here. The runtime evaluates the policy in live trading and backtests; user strategy code supplies inputs and consumes planned orders without owning the algorithms.