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.
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
configdictionary in the constructor. - State Management: Strategies can maintain persistent state across executions
by using
self.statedirectly. The execution engine automatically persists and restoresself.statebetween 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:
getSecurityUniverseSelector(): Define which securities the strategy evaluates.
Optional Implementation
Subclasses may override:
onMarketData(): Handle market data updates (requires@data_triggerdecorator).- 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 toself.statedirectly. - 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))
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.
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.
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.
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")
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}")
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.
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)
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
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
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.