gsTradingEngine Example Library v2.0
This page complements the technical reference with ready-to-study trading systems. Each example is intentionally simple, so users can copy one, run it, and then evolve it step by step.
Learn By Pattern
Examples are grouped by logic: crossover, mean reversion, breakout, portfolio and ranked universe.
Safe To Modify
Each strategy is small enough to customize without getting lost in boilerplate.
v2 Included
The library includes examples using UniverseFilter, RankedUniverseOptions and SetScore().
How To Use These Examples
Start from the example closest to your idea. First run it unchanged, then make one small edit at a time: change the ticker list, the date range, the entry filter, then the exits. This makes debugging much easier.
How To Build Your First Custom Trading Strategy
If you are new to algorithmic trading strategy development, do not start from a blank editor. Start from one of the examples below and evolve it in four small steps.
SMA Crossover Beginner
public override void ConfigureSystem()
{
// We backtest a single ticker first so the strategy is easy to understand.
Config.Tickers = new[] { "AAPL" };
// Define the historical window used by the engine.
Config.FromDate = new DateTime(2019, 1, 1);
Config.ToDate = new DateTime(2024, 12, 31);
// Start with a simple fixed-size account and fixed-size trades.
Config.StartEquity = 10000;
Config.InvestmentPerTrade = 10000;
// Execute on the next bar open for slightly more realistic fills.
Config.DefaultExecutionType = ExecutionType.NextOpen;
}
public override void ExecuteStrategy(string? ticker = null)
{
// Compute the fast and slow moving averages on the current ticker.
double sma20 = GetSMA(20);
double sma50 = GetSMA(50);
// Check whether we already have an open trade on this ticker.
bool inPos = OpenPositions.ContainsKey(CurrentTicker);
// Entry rule:
// buy when the short average is above the long average
// and we are currently flat.
if (!inPos && sma20 > sma50)
Buy("SMA20 crossed above SMA50");
// Exit rule:
// sell when the short average falls back below the long average.
if (inPos && sma20 < sma50)
Sell("SMA20 crossed below SMA50");
// Protective stop:
// if the position loses 6% from entry, close it automatically.
SetStopLoss(6.0);
}
RSI Mean Reversion Beginner
public override void ConfigureSystem()
{
// Use one liquid ticker while learning the mean reversion pattern.
Config.Tickers = new[] { "MSFT" };
Config.FromDate = new DateTime(2020, 1, 1);
Config.ToDate = new DateTime(2024, 12, 31);
// Fixed capital and fixed position size keep the example easy to read.
Config.StartEquity = 10000;
Config.InvestmentPerTrade = 10000;
}
public override void ExecuteStrategy(string? ticker = null)
{
// RSI measures short-term stretch.
// Values below 30 are often interpreted as oversold.
double rsi = GetRSI(14);
bool inPos = OpenPositions.ContainsKey(CurrentTicker);
// Entry:
// open a long trade only when the market is oversold
// and there is no existing position.
if (!inPos && rsi < 30)
Buy("RSI oversold");
// Exit:
// once RSI has reverted back to a more neutral zone, lock the trade.
if (inPos && rsi > 60)
Sell("RSI mean reversion complete");
// A fixed stop keeps losing trades under control.
SetStopLoss(5.0);
}
Breakout Above 50-Day High Beginner
public override void ConfigureSystem()
{
// Breakout logic works well on a single symbol while testing the idea.
Config.Tickers = new[] { "NVDA" };
Config.FromDate = new DateTime(2018, 1, 1);
Config.ToDate = new DateTime(2024, 12, 31);
Config.StartEquity = 10000;
Config.InvestmentPerTrade = 10000;
}
public override void ExecuteStrategy(string? ticker = null)
{
// Highest high over the recent 50 bars.
double highest50 = GetHighest(50);
// Shorter moving average used as a trend confirmation filter.
double sma20 = GetSMA(20);
bool inPos = OpenPositions.ContainsKey(CurrentTicker);
// Entry:
// buy only if price is breaking out to a 50-bar high
// and is also above the 20-day moving average.
if (!inPos && Close() >= highest50 && Close() > sma20)
Buy("50-day breakout");
// Exit:
// if price loses the short-term trend, close the trade.
if (inPos && Close() < sma20)
Sell("Lost short-term trend");
// Trailing stop lets winners run while protecting open profit.
SetTrailingStop(10.0);
}
Trend Following Long/Short Intermediate
public override void ExecuteStrategy(string? ticker = null)
{
// Use three moving averages to define trend direction.
double sma20 = GetSMA(20);
double sma50 = GetSMA(50);
double sma200 = GetSMA(200);
// Bullish alignment means short > medium > long.
bool longEntry = sma20 > sma50 && sma50 > sma200;
// Bearish alignment means short < medium < long.
bool shortEntry = sma20 < sma50 && sma50 < sma200;
// Detect whether the currently open trade is long or short.
bool hasLong = OpenPositions.TryGetValue(CurrentTicker, out var p) && p.PositionSide == 1;
bool hasShort = OpenPositions.TryGetValue(CurrentTicker, out var p2) && p2.PositionSide == -1;
// Exit first when the original trend condition is no longer true.
if (!longEntry && hasLong) Sell("Trend reversal");
if (!shortEntry && hasShort) Cover("Trend reversal");
// Then allow a new entry only if we are not already in the opposite side.
if (longEntry && !hasLong && !hasShort) Buy("Bullish alignment");
if (shortEntry && !hasShort && !hasLong) Short("Bearish alignment");
// Use one symmetric stop for both long and short positions.
SetStopLoss(6.0);
}
Multi-Ticker Portfolio Intermediate
public override void ConfigureSystem()
{
// Here we intentionally use several tickers,
// because the goal is to learn portfolio-style execution.
Config.Tickers = new[] { "AAPL", "MSFT", "GOOGL", "AMZN", "NVDA" };
Config.FromDate = new DateTime(2020, 1, 1);
Config.ToDate = new DateTime(2024, 12, 31);
Config.StartEquity = 50000;
// Instead of using a fixed dollar amount,
// invest 20% of current equity per new trade.
Config.InvestmentPerTradePercTotalEquity = 20;
// DateFirst means:
// for each date, evaluate all tickers before moving to the next date.
Config.Mode = BacktestMode.DateFirst;
}
public override void ExecuteStrategy(string? ticker = null)
{
// Same logic is applied independently to every ticker in the list.
double rsi = GetRSI(14);
double sma50 = GetSMA(50);
double sma200 = GetSMA(200);
bool inPos = OpenPositions.ContainsKey(CurrentTicker);
// Entry:
// require trend confirmation plus decent momentum.
if (!inPos && Close() > sma50 && sma50 > sma200 && rsi > 50 && rsi < 75)
Buy($"Momentum entry {CurrentTicker}");
// Exit:
// close if trend weakens or momentum fades.
if (inPos && (Close() < sma50 || rsi < 40))
Sell("Exit: trend weakened");
// Portfolio systems still need a per-position stop.
SetStopLoss(6.0);
}
Ranked S&P 500 Universe v2Advanced
public override void ConfigureSystem()
{
// v2 feature:
// instead of manually listing symbols,
// ask the engine to resolve the universe from ticker metadata.
Config.Universe = new UniverseFilter
{
PartOf = "S&P 500",
Category = "Stock",
EnabledOnly = true
};
// v2 feature:
// ranked mode will collect scores and buy the best names automatically.
Config.RankedUniverse = new RankedUniverseOptions
{
Enabled = true,
MaxPositions = 5,
RebalanceFrequency = "Weekly",
MinScore = 20
};
// Ranked strategies should usually share one common cash account.
Config.UnifiedPortfolio = true;
// Large starting capital makes it easier to hold several names at once.
Config.FromDate = new DateTime(2022, 1, 1);
Config.ToDate = new DateTime(2024, 12, 31);
Config.StartEquity = 100000;
// With 5 positions max, 20% each creates an approximately equal-weight portfolio.
Config.InvestmentPerTradePercTotalEquity = 20;
}
public override void ExecuteStrategy(string? ticker = null)
{
// Compute a few simple factors used to score each stock.
double sma50 = GetSMA(50);
double sma200 = GetSMA(200);
double rsi = GetRSI(14);
double momentum60 = GetChangePercent(60);
// Skip symbols that do not yet have enough lookback history.
if (sma50 <= 0 || sma200 <= 0)
{
ClearScore();
return;
}
// Start from zero and add points for each positive condition.
double score = 0;
// Condition 1: price above long-term trend.
if (Close() > sma200) score += 10;
// Condition 2: medium-term trend also positive.
if (sma50 > sma200) score += 10;
// Condition 3: RSI in a constructive but not overbought zone.
if (rsi >= 50 && rsi <= 70) score += 10;
// Add raw medium-term momentum as a final differentiator between stocks.
score += momentum60;
// If the score is good enough, register it.
// The engine will later compare all scores and buy the top names.
if (score >= 20)
SetScore(score, $"score={score:F1}");
else
// If the stock is not strong enough, remove it from the candidate list.
ClearScore();
}
Config.UnifiedPortfolio = true, otherwise the idea of selecting the best names in one portfolio becomes inconsistent.
Multi-Timeframe Pullback Intermediate
public override void ExecuteStrategy(string? ticker = null)
{
// Weekly averages are used as the higher-timeframe filter.
double weeklySMA20 = GetSMA(20, "w");
double weeklySMA50 = GetSMA(50, "w");
bool weeklyUp = weeklySMA20 > weeklySMA50;
// Daily values are used for the actual timing of the trade.
double dailyRSI = GetRSI(14);
double dailySMA20 = GetSMA(20);
bool inPos = OpenPositions.ContainsKey(CurrentTicker);
// Entry only if the higher timeframe is bullish
// and the daily chart is coming out of a pullback.
if (!inPos && weeklyUp && dailyRSI < 45 && Close() > dailySMA20)
Buy("Daily pullback in weekly uptrend");
if (inPos)
{
// If the higher-timeframe trend breaks, exit immediately.
if (!weeklyUp) Sell("Weekly trend broken");
// ATR stop adapts the stop distance to volatility.
SetATRStop(2.0, 14);
}
}
Risk Managed Swing Strategy Intermediate
public override void ConfigureSystem()
{
// One ticker is enough to illustrate position management techniques.
Config.Tickers = new[] { "META" };
Config.FromDate = new DateTime(2020, 1, 1);
Config.ToDate = new DateTime(2024, 12, 31);
Config.StartEquity = 10000;
Config.InvestmentPerTrade = 10000;
}
public override void ExecuteStrategy(string? ticker = null)
{
// Entry setup:
// look for RSI recovery while price is already above SMA50.
double rsi = GetRSI(14);
double sma50 = GetSMA(50);
bool inPos = OpenPositions.ContainsKey(CurrentTicker);
if (!inPos && rsi > 35 && rsi < 55 && Close() > sma50)
{
Buy("RSI recovery above SMA50");
return;
}
// If we are flat, nothing else to do on this bar.
if (!inPos) return;
// First check whether profit target has been reached.
if (SetTakeProfit(18.0)) return;
// Once the trade is in decent profit,
// switch from fixed stop to trailing stop.
if (currentGainPercent() > 5.0)
{
if (SetTrailingStop(8.0)) return;
}
else
{
// Before the trade is strongly profitable,
// protect it with a tighter fixed stop.
if (SetStopLoss(4.0)) return;
}
// Time exit:
// if the move does not develop within 25 days, close it.
if (GetDaysInPosition() > 25)
Sell("Time exit");
}
FAQ
Which trading strategy example should I start with?
Start with SMA Crossover if you want the simplest trend-following system, or RSI Mean Reversion if you want a simple reversal strategy. Both are short, readable and easy to modify.
Which examples use the new gsTradingEngine v2 features?
The best v2 example is Ranked S&P 500 Universe. It demonstrates dynamic universe resolution with UniverseFilter, ranked top-N selection with RankedUniverseOptions and cross-sectional scoring with SetScore().
How can I create a portfolio rotation strategy?
Start from Multi-Ticker Portfolio for fixed ticker lists, then move to Ranked S&P 500 Universe if you want the engine to compare many stocks and rotate into the strongest names automatically.