investfly.models.strategy
Strategy execution models and related utilities.
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:
on_market_data(): Handle market data updates (requires@data_triggerdecorator).on_schedule(): Handle scheduled time-based events (requires@time_triggerdecorator).getStandardCloseCondition(): Define automatic exit conditions.
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. - context (
StrategyExecutionContext): Execution context containing portfolio and universe.
Example
A simple moving average crossover strategy:
@data_trigger(type=DataType.BARS, barInterval=BarInterval.ONE_DAY)
class SMACrossoverStrategy(TradingStrategy):
def getSecurityUniverseSelector(self):
return SecurityUniverseSelector.fromStandardList(StandardSymbolsList.SP_100)
def on_market_data(self, updated_securities):
orders = []
for security in updated_securities:
sma_fast = self.dataService.computeIndicatorSeries(
"SMA", security, {"period": 10, "barInterval": BarInterval.ONE_DAY}
)
sma_slow = self.dataService.computeIndicatorSeries(
"SMA", security, {"period": 20, "barInterval": BarInterval.ONE_DAY}
)
if sma_fast.cross_over(sma_slow):
orders.append(TradeOrder(security, TradeType.BUY, OrderType.MARKET_ORDER))
return orders
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}")
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: updated_securities: 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 on_market_data(self, updated_securities):
orders = []
for security in updated_securities:
# 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.
This optional method is called at scheduled intervals. To enable this
callback, override this method and decorate it with @time_trigger.
The @time_trigger decorator specifies when this method should be called:
- type (
MarketTime): The frequency of scheduled execution. Options:DAILY,HOURLY,WEEKLY,MONTHLY. - time (
str, optional): Specific wall-clock time in "HH:MM:SS" format (24-hour). Only applicable forMarketTime.DAILY. If not specified, executes at market open.
Returns: Optional list of TradeOrder objects to execute. Return None or an empty list if no trades should be placed.
Note:
- The time parameter is only valid for MarketTime.DAILY.
- Time format must be "HH:MM:SS" in 24-hour format (e.g., "14:30:00" for 2:30 PM).
- Multiple @time_trigger decorators can be applied to trigger at
multiple times.
Example:
# Execute daily at 10:00 AM
@time_trigger(type=MarketTime.DAILY, time="10:00:00")
def on_schedule(self):
# Rebalance portfolio at market open
orders = []
portfolio = self.getPortfolio()
# ... rebalancing logic ...
return orders
# Execute hourly
@time_trigger(type=MarketTime.HOURLY)
def on_schedule(self):
# Check positions every hour
return None
Define standard exit conditions for closing positions automatically.
This method provides a convenient way to specify common exit criteria that apply to all positions opened by your strategy. These conditions are evaluated automatically by the platform and executed as market orders when triggered.
Standard Exit Conditions
Target Profit: Close position when profit reaches target percentage.
- Specify as positive percentage (0-100 range).
- Example:
15.0means close when position is up 15%.
Stop Loss: Close position when loss reaches threshold.
- Specify as negative percentage.
- Example:
-5.0means close when position is down 5%.
Trailing Stop: Close position when price falls by percentage from peak.
- Specify as negative percentage.
- Dynamically adjusts as position becomes profitable.
- Example:
-3.0means close if price drops 3% from highest point reached.
Timeout: Close position after specified time duration.
- Specify using TimeDelta with TimeUnit (MINUTES, HOURS, DAYS).
- Example:
TimeDelta(value=5, unit=TimeUnit.DAYS)closes after 5 days.
Returns: StandardCloseCriteria object with exit conditions, or None if no standard close conditions should be applied.
Note:
- All standard close conditions are executed as MARKET_ORDER for immediate execution.
- Multiple conditions can be combined; the first one to trigger closes the position.
- Return None (default) if you don't want any standard close conditions.
- For more complex exit logic, implement custom logic in on_market_data().
Example:
# Simple stop loss and take profit
def getStandardCloseCondition(self):
return StandardCloseCriteria(
targetProfit=10.0, # Take profit at +10%
stopLoss=-5.0, # Stop loss at -5%
trailingStop=None,
timeOut=None
)
# Trailing stop with timeout
def getStandardCloseCondition(self):
return StandardCloseCriteria(
targetProfit=None,
stopLoss=None,
trailingStop=-3.0, # Trail 3% from peak
timeOut=TimeDelta(value=7, unit=TimeUnit.DAYS)
)
# Comprehensive risk management
def getStandardCloseCondition(self):
return StandardCloseCriteria(
targetProfit=20.0, # Take profit at +20%
stopLoss=-8.0, # Stop loss at -8%
trailingStop=-4.0, # Trail 4% from peak
timeOut=TimeDelta(value=30, unit=TimeUnit.DAYS)
)
Interface for accessing market data and indicators from strategies.
StrategyDataService is an abstract base class that defines the interface for accessing various types of market data and computing technical indicators from within trading strategies.
This service provides access to:
- Technical indicators (SMA, RSI, MACD, etc.)
- Market data (quotes, bars, financials)
- News and fundamental data
The service is implemented by the execution engine and injected into
strategy instances via TradingStrategy.setDataService().
Attributes:
ALL_BARS: Constant value (-1) used with getBars() to retrieve all
available historical bars for a security.
Example:
Strategies access the data service via self.dataService:
```python
Compute indicators
sma = self.dataService.computeIndicatorSeries( "SMA", security, {"period": 20, "barInterval": BarInterval.ONE_DAY} )
Get current quote
quote = self.dataService.getQuote(security) if quote: current_price = quote.lastPrice
Get historical bars
bars = self.dataService.getBars( security, BarInterval.ONE_DAY, numBars=50 ) ```
Compute a technical indicator series for a given security.
This method computes a technical indicator (e.g., SMA, RSI, MACD) and returns a series of indicator values over time. The series can be used for analysis, signal generation, and crossover detection.
The indicatorId parameter is declared as a string (not an enum) to
support both standard indicators provided by Investfly and custom
indicators defined by users. For standard indicators, the string value
must match one of the values from the StandardIndicatorId enum.
Args:
indicatorId: The identifier of the indicator to compute. Must be a
string value. For standard indicators supported by Investfly,
use one of the values from StandardIndicatorId enum:
**Moving Averages:**
- "SMA": Simple Moving Average
- "EMA": Exponential Moving Average
**Momentum Indicators:**
- "RSI": Relative Strength Index
- "ROC": Rate of Change
- "CMO": Chande Momentum Oscillator
- "CMO_SMOOTHED": Smoothed CMO
- "PPO": Percentage Price Oscillator
- "ULTIMATE_OSC": Ultimate Oscillator
**Trend Indicators:**
- "MACD": Moving Average Convergence Divergence
- "MACDS": MACD Signal Line
- "ADX": Average Directional Index
- "PLUS_DI": Plus Directional Indicator
- "MINUS_DI": Minus Directional Indicator
- "PSAR": Parabolic SAR
**Volatility Indicators:**
- "ATR": Average True Range
- "STD_DEV": Standard Deviation
- "UPPER_BBAND": Upper Bollinger Band
- "LOWER_BBAND": Lower Bollinger Band
- "BBAND": Bollinger Bands (composite, for charts only)
**Oscillators:**
- "CCI": Commodity Channel Index
- "WILLIAM_R": Williams' %R
- "FAST_STOCHASTIC_OSC": Fast Stochastic Oscillator
- "SLOW_STOCHASTIC_OSC": Slow Stochastic Oscillator
- "STOCHASTICS": Stochastic (composite, for charts only)
**Price Indicators:**
- "MEDIAN_PRICE": Median Price
- "TYPICAL_PRICE": Typical Price
- "MAX": Maximum value over period
- "MIN": Minimum value over period
**Candlestick Patterns:**
- "DOJI": Doji pattern
- "HAMMER": Hammer pattern
- "INVERTED_HAMMER": Inverted Hammer pattern
- "DRAGONFLY_DOJI": Dragonfly Doji pattern
- "GRAVESTONE_DOJI": Gravestone Doji pattern
- "HANGING_MAN": Hanging Man pattern
- "BULLISH": Bullish pattern
- "BEARISH": Bearish pattern
**Support/Resistance:**
- "SUPPORT": Support level
- "RESISTANCE": Resistance level
**Other:**
- "AVGVOLUME": Average Volume
- "HIGH52WEEK": 52-Week High
- "LOW52WEEK": 52-Week Low
- "DRAWDOWN": Drawdown
- "PRICECHANGEPCT": Price Change Percentage
For custom indicators, use the custom indicator ID string as
defined when the indicator was created.
security: The security for which to compute the indicator.
params: Dictionary of parameters for the indicator computation.
Common parameters include:
- "period" (int): Period for the indicator (e.g., 20 for SMA(20)).
- "barInterval" (BarInterval): Bar interval for the data source.
- Indicator-specific parameters (e.g., "fast_period", "slow_period"
for MACD).
Returns:
IndicatorSeries object containing the computed indicator values.
The series provides methods for:
- Accessing the latest value: series.last.value
- Converting to list: series.toList()
- Crossover detection: series.cross_over(other), series.cross_under(other)
Note:
- The indicatorId parameter is a string type (not StandardIndicatorId
enum) to support both standard and custom indicators.
- When using standard indicators, the string value must exactly match
one of the StandardIndicatorId enum values (see
investfly.models.indicator.IndicatorEnums.StandardIndicatorId).
- Custom indicators can be referenced by their custom ID string.
Example: ```python
Compute 20-period SMA on daily bars (standard indicator)
sma20 = self.dataService.computeIndicatorSeries( "SMA", # Must match StandardIndicatorId.SMA.value security, {"period": 20, "barInterval": BarInterval.ONE_DAY} )
Compute 14-period RSI (standard indicator)
rsi = self.dataService.computeIndicatorSeries( "RSI", # Must match StandardIndicatorId.RSI.value security, {"period": 14, "barInterval": BarInterval.ONE_DAY} )
Compute custom indicator
custom_indicator = self.dataService.computeIndicatorSeries( "MY_CUSTOM_INDICATOR", # Custom indicator ID security, {"param1": 10, "barInterval": BarInterval.ONE_DAY} )
Check for crossover
if sma20.cross_over(rsi): # SMA crossed above RSI - bullish signal pass
Access latest value
current_rsi = rsi.last.value ```
Retrieve fundamental financial metrics for the given symbol.
This method returns fundamental financial data such as market cap, P/E ratio, revenue, earnings, etc. for a security.
Args: symbol: The stock symbol (e.g., "AAPL", "MSFT") for which to retrieve financial data.
Returns: Dictionary mapping FinancialField enums to their corresponding float values. Returns an empty dictionary if data is unavailable.
Example:
python
financials = self.dataService.getFinancials("AAPL")
market_cap = financials.get(FinancialField.MARKET_CAP)
pe_ratio = financials.get(FinancialField.PE_RATIO)
Retrieve the latest quote for the given security.
This method returns the most recent quote data including bid/ask prices, last trade price, volume, and other market data fields.
Args: security: The Security object for which to retrieve the quote.
Returns: Quote object containing the latest market data, or None if the quote is unavailable. The Quote object provides fields such as: - lastPrice: Last trade price - bidPrice, askPrice: Current bid and ask prices - volume: Trading volume - And other quote fields (see QuoteField enum).
Example:
python
quote = self.dataService.getQuote(security)
if quote:
current_price = quote.lastPrice
volume = quote.volume
Retrieve latest news articles for the given security.
This method returns recent news articles related to the specified security. News data can be used for sentiment analysis or event-driven trading strategies.
Args: security: The Security object for which to retrieve news.
Returns: List of StockNews objects containing news articles. Returns an empty list if no news is available. Each StockNews object contains: - title: News article title - content: Article content or summary - publishedDate: Publication date - source: News source - url: Article URL
Example:
python
news = self.dataService.getNews(security)
for article in news:
print(f"{article.title} - {article.publishedDate}")
Retrieve historical bars (candlestick/OHLC data) for a security.
This method returns historical price bars at the specified interval. Bars contain open, high, low, close (OHLC) prices and volume data.
Args:
security: The Security object for which to fetch bars.
barInterval: The interval of bars to retrieve. Options include:
- BarInterval.ONE_MINUTE
- BarInterval.FIVE_MINUTE
- BarInterval.FIFTEEN_MINUTE
- BarInterval.THIRTY_MINUTE
- BarInterval.SIXTY_MINUTE
- BarInterval.ONE_DAY
numBars: Number of bars to return. Use StrategyDataService.ALL_BARS
to retrieve all available bars. Bars are returned in chronological
order (oldest first).
Returns: List of Bar objects containing OHLC data. Each Bar object contains: - open: Opening price - high: Highest price - low: Lowest price - close: Closing price - volume: Trading volume - timestamp: Bar timestamp
Example: ```python
Get last 50 daily bars
bars = self.dataService.getBars( security, BarInterval.ONE_DAY, numBars=50 )
Get all available 1-minute bars
all_bars = self.dataService.getBars( security, BarInterval.ONE_MINUTE, StrategyDataService.ALL_BARS )
Access bar data
latest_bar = bars[-1] close_price = latest_bar.close ```
Execution context for a strategy instance.
The execution context contains per-execution data that is specific to a strategy deployment. This context is created by the execution engine and injected into strategy instances before their methods are called.
The context provides deployment-agnostic access to:
- Portfolio state (positions, balances, performance)
- Universe securities (resolved list of securities to evaluate)
Attributes:
portfolio: The current portfolio state containing:
- Open and closed positions
- Account balances (cash, equity, total value)
- Portfolio performance metrics
universe_securities: 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
TradingStrategy.getSecurityUniverseSelector().
Example: The execution engine creates and injects the context:
```python
context = StrategyExecutionContext( portfolio=current_portfolio, universe_securities=resolved_securities ) strategy.setContext(context)
Strategies access context data via convenience methods:
```python
portfolio = self.getPortfolio()
universe = self.getUniverseSecurities()
Helper class that provides a standard way to create an ABC using inheritance.
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.
Enum for scheduled time-based triggers
Type definition for data trigger information
Type definition for time trigger information
Decorator to mark a method to run on market data events.
Returns a tuple (wrapper_func, trigger_info) similar to @DataParams pattern.
Parameters: type (DataType): BARS, QUOTE, or other DataType barInterval (BarInterval, optional): Interval for BARS events
Usage: @data_trigger(type=DataType.BARS, barInterval=BarInterval.ONE_MINUTE) def on_market_data(self): ...
Decorator to mark a method to run on scheduled events.
Returns a tuple (wrapper_func, trigger_info) similar to @DataParams pattern.
Parameters: type (MarketTime): DAILY, HOURLY, etc. time (str, optional): wall-clock time in "HH:MM:SS"
Usage: @time_trigger(type=MarketTime.DAILY, time="10:00:00") def on_schedule(self): ...
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.
This class is used to specify the set of stocks to use in trading strategy. You can pick one of the standard list (e.g SP100) that we provide, provide your own list with comma separated symbols list, or provide a query based on fundamental metrics like MarketCap, PE Ratio etc.
The approach used to specify the stocks. Depending on the universeType, one of the attribute below must be specified
Standard Symbol List (i.e SP500, SP100). Required if universeType is set to STANDARD_LIST
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.