Codezerg.AlgoTrader
1.0.1
dotnet add package Codezerg.AlgoTrader --version 1.0.1
NuGet\Install-Package Codezerg.AlgoTrader -Version 1.0.1
<PackageReference Include="Codezerg.AlgoTrader" Version="1.0.1" />
<PackageVersion Include="Codezerg.AlgoTrader" Version="1.0.1" />
<PackageReference Include="Codezerg.AlgoTrader" />
paket add Codezerg.AlgoTrader --version 1.0.1
#r "nuget: Codezerg.AlgoTrader, 1.0.1"
#:package Codezerg.AlgoTrader@1.0.1
#addin nuget:?package=Codezerg.AlgoTrader&version=1.0.1
#tool nuget:?package=Codezerg.AlgoTrader&version=1.0.1
Codezerg.AlgoTrader
A C# algorithmic trading library for post-market stock strategy evaluation and backtesting.
Features
- Strategy Framework: Clean, event-driven architecture for implementing trading strategies
- Technical Indicators: Built-in SMA, EMA, RSI, MACD, Bollinger Bands, ATR
- Backtesting Engine: Full-featured backtesting with performance metrics
- Multiple Data Sources: CSV files and EODHD API support
- Performance Analytics: Sharpe ratio, max drawdown, win rate, profit factor, and more
Design Constraints
AlgoTrader is intentionally designed with specific constraints to focus on strategy evaluation:
| Constraint | Description |
|---|---|
| Cash accounts only | No margin trading |
| Long positions only | No short selling |
| Daily timeframe and above | D1, W1, MN1 only |
| Bar data only | No tick data support |
| Complete fills only | No partial order fills |
| Post-market execution | Orders execute at bar close |
Installation
NuGet Package
dotnet add package Codezerg.AlgoTrader
Or via the Package Manager:
Install-Package Codezerg.AlgoTrader
Requirements
- .NET 8.0 or later
- (Optional) EODHD API key for live data
Build from Source
dotnet build
Run Tests
dotnet test
Quick Start
1. Create a Strategy
using AlgoTrader.Indicators;
using AlgoTrader.Strategy;
public class GoldenCrossStrategy : StrategyBase
{
private iSMA? _fastMa;
private iSMA? _slowMa;
// Anchor symbol - determines when OnEvaluate() fires
public override string Symbol => "SPY";
protected override void OnInit()
{
// Initialize indicators
_fastMa = new iSMA(Close, 50);
_slowMa = new iSMA(Close, 200);
}
protected override void OnEvaluate()
{
// Update indicators
_fastMa!.Update();
_slowMa!.Update();
if (!_fastMa.Ready || !_slowMa.Ready)
return;
// Golden cross: fast MA crosses above slow MA
var goldenCross = _fastMa[1] <= _slowMa[1] && _fastMa[0] > _slowMa[0];
// Death cross: fast MA crosses below slow MA
var deathCross = _fastMa[1] >= _slowMa[1] && _fastMa[0] < _slowMa[0];
if (goldenCross && !HasPosition(Symbol))
{
var shares = SharesForAmount(Symbol, EquityPercent(95m));
if (shares > 0)
Buy(Symbol, shares);
}
else if (deathCross && HasPosition(Symbol))
{
ClosePosition(Symbol);
}
}
}
2. Run a Backtest
using AlgoTrader.Broker;
using AlgoTrader.Core;
using AlgoTrader.Data;
// Create data provider (CSV or EODHD)
var dataProvider = new CsvDataProvider("./data");
// Or: var dataProvider = new EodhdDataProvider(apiKey);
// Create broker with initial capital
var broker = new BacktestBroker(100_000m, commissionPerShare: 0.01m);
// Configure engine
var config = new EngineConfig
{
Timeframe = Timeframe.D1,
WarmupBars = 200 // Allow indicators to initialize
};
var engine = new Engine(dataProvider, broker, config);
engine.AddStrategy(new GoldenCrossStrategy());
// Run backtest
var result = await engine.RunAsync("2020-01-01", "2024-12-31");
// Print results
Console.WriteLine(result.Summary());
3. View Results
═══════════════════════════════════════════════════════════════
BACKTEST RESULTS
═══════════════════════════════════════════════════════════════
Period: 2020-01-01 to 2024-12-31
Initial Equity: $100,000.00
Final Equity: $142,350.00
───────────────────────────────────────────────────────────────
RETURNS
───────────────────────────────────────────────────────────────
Total Return: 42.35% ($42,350.00)
Annualized: 8.47%
───────────────────────────────────────────────────────────────
RISK
───────────────────────────────────────────────────────────────
Sharpe Ratio: 0.85
Max Drawdown: 15.23% ($15,230.00)
───────────────────────────────────────────────────────────────
TRADES
───────────────────────────────────────────────────────────────
Total Trades: 12
Win Rate: 58.33% (7W / 5L)
Profit Factor: 2.15
Architecture
Strategy Layer (user strategies)
↓
Engine Layer (orchestrates execution)
↓
┌───┴───┐
↓ ↓
Data Broker
Project Structure
src/AlgoTrader/
├── Core/ # Engine, types, events, configuration
├── Domain/ # Bar, Order, Position, Account
├── Data/ # DataSeries, SymbolData, providers
├── Broker/ # IBroker, BacktestBroker, Trade
├── Strategy/ # StrategyBase, StrategyContext
├── Indicators/ # SMA, EMA, RSI, MACD, Bands, ATR
└── Analysis/ # Metrics, BacktestResult
examples/ # Example strategies
tests/ # Unit tests
Strategy Development
Strategy Lifecycle
public class MyStrategy : StrategyBase
{
public override string Symbol => "SPY"; // Anchor symbol
protected override void OnInit()
{
// Called once at start - initialize indicators
}
protected override void OnEvaluate()
{
// Called after each bar - implement trading logic
}
protected override void OnDeinit()
{
// Called once at end - cleanup
}
}
Data Access
Data is accessed using reverse indexing where 0 is the most recent bar:
// Anchor symbol data (shorthand)
Close[0] // Current bar close
Close[1] // Previous bar close
Open[0] // Current bar open
High[0] // Current bar high
Low[0] // Current bar low
Volume(0) // Current bar volume
// Multi-symbol data access
Data["AAPL"].Close[0] // AAPL current close
Data["MSFT"].High[1] // MSFT previous high
Trading Functions
// Submit orders
Buy(symbol, quantity); // Market buy
Buy(symbol, quantity, price, OrderType.Limit); // Limit buy
Sell(symbol, quantity); // Market sell
ClosePosition(symbol); // Close entire position
// Position info
HasPosition(symbol) // Check if has position OR pending buy
PositionSize(symbol) // Current shares held
// Sizing helpers
EquityPercent(50m) // 50% of current equity
SharesForAmount(symbol, amount) // Shares purchasable for amount
Order Flow
Orders placed in OnEvaluate() are queued and execute after the method returns:
OnEvaluate() called
↓
Buy/Sell orders queued
↓
OnEvaluate() returns
↓
Orders execute at bar close price
↓
Next bar begins
Use HasPosition(symbol) to check both existing positions AND pending buy orders, preventing duplicate orders in the same evaluation cycle.
Indicators
All indicators use the i prefix naming convention:
| Indicator | Class | Description |
|---|---|---|
| SMA | iSMA |
Simple Moving Average |
| EMA | iEMA |
Exponential Moving Average |
| RSI | iRSI |
Relative Strength Index (Wilder's smoothing) |
| MACD | iMACD |
Moving Average Convergence Divergence |
| Bollinger Bands | iBands |
Bollinger Bands (SMA +/- std dev) |
| ATR | iATR |
Average True Range |
Usage Example
protected override void OnInit()
{
// Moving averages
var sma = new iSMA(Close, 20);
var ema = new iEMA(Close, 12);
// RSI
var rsi = new iRSI(Close, 14);
// MACD
var macd = new iMACD(Close, 12, 26, 9);
// Bollinger Bands
var bands = new iBands(Close, 20, 2.0m);
// ATR (requires SymbolData)
var atr = new iATR(Data["SPY"], 14);
}
protected override void OnEvaluate()
{
// Update indicators
sma.Update();
ema.Update();
rsi.Update();
macd.Update();
bands.Update();
atr.Update();
// Access values
if (sma.Ready)
{
var currentSma = sma[0];
var previousSma = sma[1];
}
// MACD lines
if (macd.Ready)
{
var macdLine = macd.Main[0];
var signalLine = macd.Signal[0];
var histogram = macd.Histogram[0];
}
// Bollinger Bands
if (bands.Ready)
{
var upper = bands.Upper[0];
var middle = bands.Middle[0];
var lower = bands.Lower[0];
}
}
Data Providers
CSV Data Provider
Reads from local CSV files with naming convention {SYMBOL}_{TIMEFRAME}.csv:
data/
├── SPY_D1.csv
├── AAPL_D1.csv
└── MSFT_D1.csv
CSV Format:
date,open,high,low,close,volume
2020-01-02,323.54,324.89,322.53,324.87,32145000
2020-01-03,321.16,323.64,321.05,322.41,36156000
Usage:
var provider = new CsvDataProvider("./data");
EODHD Data Provider
Fetches data from the EODHD API:
// Set environment variable
// Windows: set EODHD_API_KEY=your_api_key
// Linux/Mac: export EODHD_API_KEY=your_api_key
var apiKey = Environment.GetEnvironmentVariable("EODHD_API_KEY");
await using var provider = new EodhdDataProvider(apiKey);
Account State Persistence
Account state (balance, positions, orders, trades) can be saved and loaded for two main use cases:
- Backtesting/Development - Resume backtests or analyze results
- Daily Execution - Load state, execute latest bar, save state
using Codezerg.AlgoTrader.Domain;
// Load existing account or create new one
Account account;
if (File.Exists("account.json"))
account = Account.LoadFromFile("account.json");
else
account = new Account(100_000m);
// Create broker with the account
var broker = new BacktestBroker(account, commissionPerShare: 0.01m);
// ... run strategy ...
// Save account state
account.SaveToFile("account.json");
Saved State Format (JSON):
{
"Balance": 95234.50,
"Positions": [
{
"Symbol": "SPY",
"Quantity": 100,
"AvgPrice": 450.25,
"OpenedAt": "2024-01-15T00:00:00Z",
"MarketPrice": 455.80
}
],
"Orders": [],
"Trades": [
{
"OrderId": "abc-123",
"Symbol": "SPY",
"Side": "Buy",
"Quantity": 100,
"Price": 450.25,
"Timestamp": "2024-01-15T00:00:00Z",
"Commission": 1.00,
"Pnl": 0
}
]
}
Example Strategies
The examples/ directory contains ready-to-use strategies:
| Strategy | Description |
|---|---|
BuyAndHoldStrategy |
Simple benchmark - buy once and hold |
GoldenCrossStrategy |
50/200 SMA crossover |
MeanReversionStrategy |
RSI oversold/overbought with trend filter |
MomentumStrategy |
Multi-symbol momentum rotation |
Running Examples
# Set API key (required for EODHD data)
export EODHD_API_KEY=your_api_key
# Run default example
dotnet run --project examples/Codezerg.AlgoTrader.Examples.csproj
# Run stateful example (demonstrates account persistence)
dotnet run --project examples/Codezerg.AlgoTrader.Examples.csproj -- --stateful
Performance Metrics
The backtest result includes comprehensive metrics:
| Metric | Description |
|---|---|
| Total Return | Percentage and dollar return |
| Annualized Return | Compound annual growth rate |
| Sharpe Ratio | Risk-adjusted return (assumes 2% risk-free rate) |
| Max Drawdown | Largest peak-to-trough decline |
| Total Trades | Number of completed round trips |
| Win Rate | Percentage of profitable trades |
| Profit Factor | Gross profit / gross loss |
| Average Win/Loss | Mean winning/losing trade |
| Largest Win/Loss | Best/worst single trade |
| Total Commission | Sum of all commissions paid |
Type Conventions
- Prices: Use
decimal(notfloat/double) for all monetary values - Timestamps: Use
DateTimefor all time-related values - Bar: Immutable
recordtype - Order/Position: Mutable classes with state management
- Broker: Async interface (HTTP-ready design)
Configuration
var config = new EngineConfig
{
Timeframe = Timeframe.D1, // D1, W1, or MN1
WarmupBars = 200 // Bars before trading starts
};
License
MIT License - see LICENSE file for details.
Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Run tests:
dotnet test - Submit a pull request
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- CsvHelper (>= 31.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.