mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-04 20:17:43 +00:00
updates from mac
This commit is contained in:
+6585
-6585
File diff suppressed because it is too large
Load Diff
@@ -1,51 +1,51 @@
|
||||
*Act:** meticulous auto-agent; finish fully—no early stop.
|
||||
Cycle: State→Do→Verify→Iterate→Validate.
|
||||
Style: concise, exact, verifiable.
|
||||
|
||||
### 🧰 Tool Roles
|
||||
| Tool | Purpose |
|
||||
|------|----------|
|
||||
| **seq-think-mcp** | plan & decompose tasks |
|
||||
| **tavily-mcp** | fresh info, web/news search |
|
||||
| **ref-tools-mcp** | lib/framework specs |
|
||||
| **wolfram-mcp** | math/logic/symbolic verify |
|
||||
| **git-mcp** | code/docs search, lint, commit rules |
|
||||
| **qdrant-mcp** | long-term memory (no secrets) |
|
||||
|
||||
Fallback: search→tavily→ref→git | plan→seq | calc→wolfram | persist→qdrant.
|
||||
|
||||
### ⚙️ Workflow
|
||||
1️⃣ **Recall/Discover:** qdrant for mem, tavily/ref/wolfram for current info.
|
||||
2️⃣ **Analyze:** define expected, edges, deps, pitfalls; plan via seq-think.
|
||||
3️⃣ **Investigate:** git search→read context→root cause; log in qdrant.
|
||||
4️⃣ **Research:** tavily search→extract; ref-tools for stds; git for docs; recurse; save refs.
|
||||
5️⃣ **Plan:** seq-think build TODO (emoji status); store in qdrant.
|
||||
6️⃣ **Implement:** small testable edits; read ≤2k lines; make `.env` if missing.
|
||||
7️⃣ **Debug:** logs/probes; fix root; reverify each step.
|
||||
8️⃣ **Test:** run per change; add edges; repeat till pass.
|
||||
9️⃣ **Validate:** confirm intent; hidden tests; math check via wolfram.
|
||||
🔟 **Memorize:** store verified facts `{text,meta:{src,proj,date,tags}}` → qdrant; tag old deprecated.
|
||||
|
||||
### 💬 Comm
|
||||
Speak clear, brief, pro-casual.
|
||||
Use bullets/code; no filler.
|
||||
Write direct to files; show only if asked.
|
||||
|
||||
### 🪶 Git Policy
|
||||
No auto-commit—only on user cmd.
|
||||
Before commit:
|
||||
1) verify scope/tests ✔
|
||||
2) check rules via git + qdrant ✔
|
||||
3) `git add` → `git commit -m "<msg>"`
|
||||
Msg: subj ≤50ch, imperative; body ≤72ch what/why; footer refs/trailers.
|
||||
Checklist: concise ✔ why ✔ refs ✔ style ✔ tests ✔
|
||||
|
||||
### ⚠️ Error Handling
|
||||
If unclear → reverify (tavily/ref).
|
||||
Math gap → wolfram.
|
||||
Missing ctx → qdrant.
|
||||
Multi-path → seq-think fork.
|
||||
|
||||
### ✅ Goal
|
||||
Deliver complete, tested, verified soln; persist in qdrant.
|
||||
Loop: Plan→Exec→Verify→Persist→Confirm.
|
||||
*Act:** meticulous auto-agent; finish fully—no early stop.
|
||||
Cycle: State→Do→Verify→Iterate→Validate.
|
||||
Style: concise, exact, verifiable.
|
||||
|
||||
### 🧰 Tool Roles
|
||||
| Tool | Purpose |
|
||||
|------|----------|
|
||||
| **seq-think-mcp** | plan & decompose tasks |
|
||||
| **tavily-mcp** | fresh info, web/news search |
|
||||
| **ref-tools-mcp** | lib/framework specs |
|
||||
| **wolfram-mcp** | math/logic/symbolic verify |
|
||||
| **git-mcp** | code/docs search, lint, commit rules |
|
||||
| **qdrant-mcp** | long-term memory (no secrets) |
|
||||
|
||||
Fallback: search→tavily→ref→git | plan→seq | calc→wolfram | persist→qdrant.
|
||||
|
||||
### ⚙️ Workflow
|
||||
1️⃣ **Recall/Discover:** qdrant for mem, tavily/ref/wolfram for current info.
|
||||
2️⃣ **Analyze:** define expected, edges, deps, pitfalls; plan via seq-think.
|
||||
3️⃣ **Investigate:** git search→read context→root cause; log in qdrant.
|
||||
4️⃣ **Research:** tavily search→extract; ref-tools for stds; git for docs; recurse; save refs.
|
||||
5️⃣ **Plan:** seq-think build TODO (emoji status); store in qdrant.
|
||||
6️⃣ **Implement:** small testable edits; read ≤2k lines; make `.env` if missing.
|
||||
7️⃣ **Debug:** logs/probes; fix root; reverify each step.
|
||||
8️⃣ **Test:** run per change; add edges; repeat till pass.
|
||||
9️⃣ **Validate:** confirm intent; hidden tests; math check via wolfram.
|
||||
🔟 **Memorize:** store verified facts `{text,meta:{src,proj,date,tags}}` → qdrant; tag old deprecated.
|
||||
|
||||
### 💬 Comm
|
||||
Speak clear, brief, pro-casual.
|
||||
Use bullets/code; no filler.
|
||||
Write direct to files; show only if asked.
|
||||
|
||||
### 🪶 Git Policy
|
||||
No auto-commit—only on user cmd.
|
||||
Before commit:
|
||||
1) verify scope/tests ✔
|
||||
2) check rules via git + qdrant ✔
|
||||
3) `git add` → `git commit -m "<msg>"`
|
||||
Msg: subj ≤50ch, imperative; body ≤72ch what/why; footer refs/trailers.
|
||||
Checklist: concise ✔ why ✔ refs ✔ style ✔ tests ✔
|
||||
|
||||
### ⚠️ Error Handling
|
||||
If unclear → reverify (tavily/ref).
|
||||
Math gap → wolfram.
|
||||
Missing ctx → qdrant.
|
||||
Multi-path → seq-think fork.
|
||||
|
||||
### ✅ Goal
|
||||
Deliver complete, tested, verified soln; persist in qdrant.
|
||||
Loop: Plan→Exec→Verify→Persist→Confirm.
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
name: "CodeQL Config"
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
- uses: security-extended
|
||||
|
||||
paths-ignore:
|
||||
- '**/test/**'
|
||||
- '**/tests/**'
|
||||
- '**/*.test.cs'
|
||||
- '**/obj/**'
|
||||
- '**/bin/**'
|
||||
- '**/docs/**'
|
||||
|
||||
query-filters:
|
||||
- exclude:
|
||||
problem.severity:
|
||||
- warning
|
||||
- recommendation
|
||||
|
||||
paths:
|
||||
name: "CodeQL Config"
|
||||
|
||||
queries:
|
||||
- uses: security-and-quality
|
||||
- uses: security-extended
|
||||
|
||||
paths-ignore:
|
||||
- '**/test/**'
|
||||
- '**/tests/**'
|
||||
- '**/*.test.cs'
|
||||
- '**/obj/**'
|
||||
- '**/bin/**'
|
||||
- '**/docs/**'
|
||||
|
||||
query-filters:
|
||||
- exclude:
|
||||
problem.severity:
|
||||
- warning
|
||||
- recommendation
|
||||
|
||||
paths:
|
||||
- src
|
||||
@@ -1,222 +1,222 @@
|
||||
# QuanTAlib AI Coding Agent Instructions
|
||||
|
||||
## Project Overview
|
||||
QuanTAlib is a high-performance C# library for quantitative technical analysis, targeting .NET 8.0 with real-time streaming data processing. The library provides 50+ technical indicators optimized for sub-millisecond calculations using circular buffers, SIMD operations, and event-driven architecture.
|
||||
|
||||
## Critical Architecture Patterns
|
||||
|
||||
### Core Data Flow
|
||||
All indicators inherit from `AbstractBase` (in `lib/core/abstractBase.cs`) which implements `ITValue`:
|
||||
```csharp
|
||||
// Standard indicator lifecycle:
|
||||
Input → Calc() → ManageState(isNew) → Calculation() → Process() → Pub event
|
||||
```
|
||||
|
||||
**Key insight**: The `isNew` parameter distinguishes between new bars and updates to the last bar. Indicators must support both modes - this is tested extensively in `Tests/test_updates_*.cs`.
|
||||
|
||||
### Circular Buffer Pattern
|
||||
`CircularBuffer` (in `lib/core/circularbuffer.cs`) is the foundation for memory-efficient fixed-capacity storage:
|
||||
- Never grows beyond initial capacity
|
||||
- O(1) add/access operations
|
||||
- SIMD-optimized aggregations (Sum, Min, Max, Average)
|
||||
- **Critical**: Always use `Add(item, isNew)` - the `isNew` flag controls whether to append or update
|
||||
|
||||
### State Management in Indicators
|
||||
Every indicator must implement:
|
||||
```csharp
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew) {
|
||||
_index++;
|
||||
_p_prevValue = _prevValue; // Backup state
|
||||
} else {
|
||||
_prevValue = _p_prevValue; // Restore state
|
||||
}
|
||||
}
|
||||
```
|
||||
This allows bar updates without corrupting historical calculations.
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### MCP-Orchestrated Process
|
||||
**Research Gate**: Before implementing non-trivial indicators, use Context7 to retrieve authoritative formulas/references. Embed citation tags in PR descriptions.
|
||||
|
||||
**Decomposition**: Use Sequential-Thinking for complex multi-stage work (SIMD refactors, multi-timeframe logic).
|
||||
|
||||
**Task Tracking**: Taskmaster holds the canonical task graph. Feature branches follow pattern: `feature/{taskId}-{slug}`.
|
||||
|
||||
**Quality Gates**:
|
||||
1. Formula citation required for non-trivial indicators (Context7 tag)
|
||||
2. Benchmark data required for performance-related changes
|
||||
3. Taskmaster task IDs must be referenced in PRs
|
||||
4. Update `memory-bank/progress.md` after merge when threshold met
|
||||
|
||||
### Build & Test Commands
|
||||
```powershell
|
||||
# Build solution
|
||||
dotnet build QuanTAlib.sln
|
||||
|
||||
# Run all tests
|
||||
dotnet test --no-build
|
||||
|
||||
# Run with coverage
|
||||
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=lcov
|
||||
|
||||
# Build using tasks.json
|
||||
# Use Run Task: "build" or "test"
|
||||
```
|
||||
|
||||
### Adding a New Indicator
|
||||
1. **Research**: Get formula/specification (Context7 if needed)
|
||||
2. **Location**: Place in appropriate `lib/` subdirectory (averages, oscillators, momentum, volatility, volume, statistics)
|
||||
3. **Template structure**:
|
||||
```csharp
|
||||
using System.Runtime.CompilerServices;
|
||||
namespace QuanTAlib;
|
||||
|
||||
[SkipLocalsInit]
|
||||
public sealed class MyIndicator : AbstractBase
|
||||
{
|
||||
private CircularBuffer _buffer;
|
||||
private double _prevValue, _p_prevValue; // State + backup
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public MyIndicator(int period)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(period, 1);
|
||||
_buffer = new(period);
|
||||
WarmupPeriod = period; // Set when indicator stabilizes
|
||||
Name = $"MyIndicator({period})";
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew) {
|
||||
_index++;
|
||||
_p_prevValue = _prevValue;
|
||||
} else {
|
||||
_prevValue = _p_prevValue;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
_buffer.Add(Input.Value, Input.IsNew);
|
||||
// Implement calculation logic
|
||||
return result;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
4. **Testing**: Create update test in `Tests/test_updates_*.cs`:
|
||||
```csharp
|
||||
[Fact]
|
||||
public void MyIndicator_Update()
|
||||
{
|
||||
var indicator = new MyIndicator(period: 14);
|
||||
TestTValueUpdate(indicator, indicator.Calc);
|
||||
}
|
||||
```
|
||||
|
||||
### Quantower Integration
|
||||
For platform indicators in `quantower/`, create wrapper classes inheriting from Quantower's `Indicator`:
|
||||
- Use private `lib/` indicator instances
|
||||
- Map `OnUpdate()` to indicator's `Calc()` method
|
||||
- Extract output fields (e.g., `ma`, `jmaUp`, `jmaLo`) from indicator state
|
||||
|
||||
## Code Style Requirements
|
||||
|
||||
### Performance First
|
||||
- Use `[MethodImpl(MethodImplOptions.AggressiveInlining)]` for hot paths
|
||||
- Use `[MethodImpl(MethodImplOptions.AggressiveOptimization)]` for calculation methods
|
||||
- Apply `[SkipLocalsInit]` to indicator classes
|
||||
- Prefer SIMD operations in `CircularBuffer` for aggregations
|
||||
- Minimize allocations in `Calculation()` methods
|
||||
|
||||
### C# Conventions
|
||||
- **No inline comments** within methods - code should be self-documenting
|
||||
- Use XML doc comments for public APIs only
|
||||
- PascalCase for public members, _camelCase for private fields
|
||||
- Compact code - minimal whitespace between logical blocks
|
||||
- Latest C# features: `ArgumentOutOfRangeException.ThrowIfLessThan`, pattern matching, etc.
|
||||
|
||||
### Project Settings
|
||||
- `LangVersion: preview` - use cutting-edge C# features
|
||||
- `AllowUnsafeBlocks: true` - SIMD and unsafe operations permitted
|
||||
- `Nullable: enable` - strict nullability checking
|
||||
- Target: `net8.0`
|
||||
|
||||
## Key Files & Directories
|
||||
|
||||
### Core Library Structure
|
||||
```
|
||||
lib/
|
||||
├── core/ # AbstractBase, CircularBuffer, TSeries, TBar, TValue
|
||||
├── averages/ # Moving averages (SMA, EMA, DEMA, TEMA, JMA, etc.)
|
||||
├── oscillators/ # RSI, Stochastic, Williams %R, CCI, Fisher
|
||||
├── momentum/ # MACD, ADX, ROC, Vortex
|
||||
├── volatility/ # ATR, Bollinger Bands, volatility measures
|
||||
├── volume/ # Volume-based indicators
|
||||
└── statistics/ # Statistical measures, correlations
|
||||
```
|
||||
|
||||
### Critical Reference Files
|
||||
- `lib/core/abstractBase.cs` - Base class for all indicators
|
||||
- `lib/core/circularbuffer.cs` - Memory-efficient storage with SIMD
|
||||
- `Directory.Build.props` - Solution-wide MSBuild properties
|
||||
- `memory-bank/systemPatterns.md` - Architecture patterns
|
||||
- `memory-bank/activeContext.md` - Current work focus and MCP policies
|
||||
- `memory-bank/progress.md` - Completed features and roadmap
|
||||
|
||||
### Testing Reference
|
||||
- `Tests/test_updates_*.cs` - Update behavior validation (IsNew handling)
|
||||
- `Tests/test_quantower.cs` - Quantower integration validation
|
||||
- `Tests/test_talib.cs`, `test_Trady.cs` - Cross-validation against reference libraries
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Multi-Stage Smoothing
|
||||
Many indicators (DEMA, TEMA, MACD) use cascaded smoothing:
|
||||
```csharp
|
||||
private readonly Ema _ema1;
|
||||
private readonly Ema _ema2;
|
||||
|
||||
_ema1.Calc(Input.Value, Input.IsNew);
|
||||
_ema2.Calc(_ema1.Value, Input.IsNew);
|
||||
```
|
||||
|
||||
### Bar-Based vs Value-Based
|
||||
- **Value-based**: Accept `TValue`, process single values (most indicators)
|
||||
- **Bar-based**: Accept `TBar` (OHLCV), process bar data (ATR, Stochastic, volume indicators)
|
||||
|
||||
Override appropriate `Calc()` method:
|
||||
```csharp
|
||||
public override TValue Calc(TBar barInput) { /* ... */ }
|
||||
```
|
||||
|
||||
### WarmupPeriod Calculation
|
||||
Set `WarmupPeriod` to indicate when the indicator reaches 95% accuracy:
|
||||
```csharp
|
||||
WarmupPeriod = (int)Math.Ceiling(Math.Log(0.05) / Math.Log(1 - alpha));
|
||||
```
|
||||
|
||||
## Validation Strategy
|
||||
1. **Update tests**: Verify `isNew=false` behavior converges to `isNew=true` with same final value
|
||||
2. **Reference comparison**: Validate against TALib, Trady, or Skender implementations
|
||||
3. **Edge cases**: Test with insufficient data (< period), NaN/Infinity, extreme values
|
||||
4. **Performance**: Benchmark calculation time - target < 0.5ms per update
|
||||
|
||||
## Documentation Requirements
|
||||
- XML docs on public classes/methods describing purpose, formula, and sources
|
||||
- Mathematical formulas in doc comments with source citations
|
||||
- No internal comments - let code structure communicate intent
|
||||
- Update `memory-bank/progress.md` after significant feature completion
|
||||
|
||||
## GitVersion & Releases
|
||||
- Semantic versioning via GitVersion.yml
|
||||
- Version properties auto-injected: `$(GitVersion_MajorMinorPatch)`
|
||||
- Commit messages influence version bumps (conventional commits)
|
||||
- Build creates NuGet package with embedded version metadata
|
||||
# QuanTAlib AI Coding Agent Instructions
|
||||
|
||||
## Project Overview
|
||||
QuanTAlib is a high-performance C# library for quantitative technical analysis, targeting .NET 8.0 with real-time streaming data processing. The library provides 50+ technical indicators optimized for sub-millisecond calculations using circular buffers, SIMD operations, and event-driven architecture.
|
||||
|
||||
## Critical Architecture Patterns
|
||||
|
||||
### Core Data Flow
|
||||
All indicators inherit from `AbstractBase` (in `lib/core/abstractBase.cs`) which implements `ITValue`:
|
||||
```csharp
|
||||
// Standard indicator lifecycle:
|
||||
Input → Calc() → ManageState(isNew) → Calculation() → Process() → Pub event
|
||||
```
|
||||
|
||||
**Key insight**: The `isNew` parameter distinguishes between new bars and updates to the last bar. Indicators must support both modes - this is tested extensively in `Tests/test_updates_*.cs`.
|
||||
|
||||
### Circular Buffer Pattern
|
||||
`CircularBuffer` (in `lib/core/circularbuffer.cs`) is the foundation for memory-efficient fixed-capacity storage:
|
||||
- Never grows beyond initial capacity
|
||||
- O(1) add/access operations
|
||||
- SIMD-optimized aggregations (Sum, Min, Max, Average)
|
||||
- **Critical**: Always use `Add(item, isNew)` - the `isNew` flag controls whether to append or update
|
||||
|
||||
### State Management in Indicators
|
||||
Every indicator must implement:
|
||||
```csharp
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew) {
|
||||
_index++;
|
||||
_p_prevValue = _prevValue; // Backup state
|
||||
} else {
|
||||
_prevValue = _p_prevValue; // Restore state
|
||||
}
|
||||
}
|
||||
```
|
||||
This allows bar updates without corrupting historical calculations.
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### MCP-Orchestrated Process
|
||||
**Research Gate**: Before implementing non-trivial indicators, use Context7 to retrieve authoritative formulas/references. Embed citation tags in PR descriptions.
|
||||
|
||||
**Decomposition**: Use Sequential-Thinking for complex multi-stage work (SIMD refactors, multi-timeframe logic).
|
||||
|
||||
**Task Tracking**: Taskmaster holds the canonical task graph. Feature branches follow pattern: `feature/{taskId}-{slug}`.
|
||||
|
||||
**Quality Gates**:
|
||||
1. Formula citation required for non-trivial indicators (Context7 tag)
|
||||
2. Benchmark data required for performance-related changes
|
||||
3. Taskmaster task IDs must be referenced in PRs
|
||||
4. Update `memory-bank/progress.md` after merge when threshold met
|
||||
|
||||
### Build & Test Commands
|
||||
```powershell
|
||||
# Build solution
|
||||
dotnet build QuanTAlib.sln
|
||||
|
||||
# Run all tests
|
||||
dotnet test --no-build
|
||||
|
||||
# Run with coverage
|
||||
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=lcov
|
||||
|
||||
# Build using tasks.json
|
||||
# Use Run Task: "build" or "test"
|
||||
```
|
||||
|
||||
### Adding a New Indicator
|
||||
1. **Research**: Get formula/specification (Context7 if needed)
|
||||
2. **Location**: Place in appropriate `lib/` subdirectory (averages, oscillators, momentum, volatility, volume, statistics)
|
||||
3. **Template structure**:
|
||||
```csharp
|
||||
using System.Runtime.CompilerServices;
|
||||
namespace QuanTAlib;
|
||||
|
||||
[SkipLocalsInit]
|
||||
public sealed class MyIndicator : AbstractBase
|
||||
{
|
||||
private CircularBuffer _buffer;
|
||||
private double _prevValue, _p_prevValue; // State + backup
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public MyIndicator(int period)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfLessThan(period, 1);
|
||||
_buffer = new(period);
|
||||
WarmupPeriod = period; // Set when indicator stabilizes
|
||||
Name = $"MyIndicator({period})";
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected override void ManageState(bool isNew)
|
||||
{
|
||||
if (isNew) {
|
||||
_index++;
|
||||
_p_prevValue = _prevValue;
|
||||
} else {
|
||||
_prevValue = _p_prevValue;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
protected override double Calculation()
|
||||
{
|
||||
ManageState(Input.IsNew);
|
||||
_buffer.Add(Input.Value, Input.IsNew);
|
||||
// Implement calculation logic
|
||||
return result;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
4. **Testing**: Create update test in `Tests/test_updates_*.cs`:
|
||||
```csharp
|
||||
[Fact]
|
||||
public void MyIndicator_Update()
|
||||
{
|
||||
var indicator = new MyIndicator(period: 14);
|
||||
TestTValueUpdate(indicator, indicator.Calc);
|
||||
}
|
||||
```
|
||||
|
||||
### Quantower Integration
|
||||
For platform indicators in `quantower/`, create wrapper classes inheriting from Quantower's `Indicator`:
|
||||
- Use private `lib/` indicator instances
|
||||
- Map `OnUpdate()` to indicator's `Calc()` method
|
||||
- Extract output fields (e.g., `ma`, `jmaUp`, `jmaLo`) from indicator state
|
||||
|
||||
## Code Style Requirements
|
||||
|
||||
### Performance First
|
||||
- Use `[MethodImpl(MethodImplOptions.AggressiveInlining)]` for hot paths
|
||||
- Use `[MethodImpl(MethodImplOptions.AggressiveOptimization)]` for calculation methods
|
||||
- Apply `[SkipLocalsInit]` to indicator classes
|
||||
- Prefer SIMD operations in `CircularBuffer` for aggregations
|
||||
- Minimize allocations in `Calculation()` methods
|
||||
|
||||
### C# Conventions
|
||||
- **No inline comments** within methods - code should be self-documenting
|
||||
- Use XML doc comments for public APIs only
|
||||
- PascalCase for public members, _camelCase for private fields
|
||||
- Compact code - minimal whitespace between logical blocks
|
||||
- Latest C# features: `ArgumentOutOfRangeException.ThrowIfLessThan`, pattern matching, etc.
|
||||
|
||||
### Project Settings
|
||||
- `LangVersion: preview` - use cutting-edge C# features
|
||||
- `AllowUnsafeBlocks: true` - SIMD and unsafe operations permitted
|
||||
- `Nullable: enable` - strict nullability checking
|
||||
- Target: `net8.0`
|
||||
|
||||
## Key Files & Directories
|
||||
|
||||
### Core Library Structure
|
||||
```
|
||||
lib/
|
||||
├── core/ # AbstractBase, CircularBuffer, TSeries, TBar, TValue
|
||||
├── averages/ # Moving averages (SMA, EMA, DEMA, TEMA, JMA, etc.)
|
||||
├── oscillators/ # RSI, Stochastic, Williams %R, CCI, Fisher
|
||||
├── momentum/ # MACD, ADX, ROC, Vortex
|
||||
├── volatility/ # ATR, Bollinger Bands, volatility measures
|
||||
├── volume/ # Volume-based indicators
|
||||
└── statistics/ # Statistical measures, correlations
|
||||
```
|
||||
|
||||
### Critical Reference Files
|
||||
- `lib/core/abstractBase.cs` - Base class for all indicators
|
||||
- `lib/core/circularbuffer.cs` - Memory-efficient storage with SIMD
|
||||
- `Directory.Build.props` - Solution-wide MSBuild properties
|
||||
- `memory-bank/systemPatterns.md` - Architecture patterns
|
||||
- `memory-bank/activeContext.md` - Current work focus and MCP policies
|
||||
- `memory-bank/progress.md` - Completed features and roadmap
|
||||
|
||||
### Testing Reference
|
||||
- `Tests/test_updates_*.cs` - Update behavior validation (IsNew handling)
|
||||
- `Tests/test_quantower.cs` - Quantower integration validation
|
||||
- `Tests/test_talib.cs`, `test_Trady.cs` - Cross-validation against reference libraries
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Multi-Stage Smoothing
|
||||
Many indicators (DEMA, TEMA, MACD) use cascaded smoothing:
|
||||
```csharp
|
||||
private readonly Ema _ema1;
|
||||
private readonly Ema _ema2;
|
||||
|
||||
_ema1.Calc(Input.Value, Input.IsNew);
|
||||
_ema2.Calc(_ema1.Value, Input.IsNew);
|
||||
```
|
||||
|
||||
### Bar-Based vs Value-Based
|
||||
- **Value-based**: Accept `TValue`, process single values (most indicators)
|
||||
- **Bar-based**: Accept `TBar` (OHLCV), process bar data (ATR, Stochastic, volume indicators)
|
||||
|
||||
Override appropriate `Calc()` method:
|
||||
```csharp
|
||||
public override TValue Calc(TBar barInput) { /* ... */ }
|
||||
```
|
||||
|
||||
### WarmupPeriod Calculation
|
||||
Set `WarmupPeriod` to indicate when the indicator reaches 95% accuracy:
|
||||
```csharp
|
||||
WarmupPeriod = (int)Math.Ceiling(Math.Log(0.05) / Math.Log(1 - alpha));
|
||||
```
|
||||
|
||||
## Validation Strategy
|
||||
1. **Update tests**: Verify `isNew=false` behavior converges to `isNew=true` with same final value
|
||||
2. **Reference comparison**: Validate against TALib, Trady, or Skender implementations
|
||||
3. **Edge cases**: Test with insufficient data (< period), NaN/Infinity, extreme values
|
||||
4. **Performance**: Benchmark calculation time - target < 0.5ms per update
|
||||
|
||||
## Documentation Requirements
|
||||
- XML docs on public classes/methods describing purpose, formula, and sources
|
||||
- Mathematical formulas in doc comments with source citations
|
||||
- No internal comments - let code structure communicate intent
|
||||
- Update `memory-bank/progress.md` after significant feature completion
|
||||
|
||||
## GitVersion & Releases
|
||||
- Semantic versioning via GitVersion.yml
|
||||
- Version properties auto-injected: `$(GitVersion_MajorMinorPatch)`
|
||||
- Commit messages influence version bumps (conventional commits)
|
||||
- Build creates NuGet package with embedded version metadata
|
||||
|
||||
+342
-342
@@ -1,342 +1,342 @@
|
||||
name: Publish Workflow
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'docs/**'
|
||||
- '.gitignore'
|
||||
- 'LICENSE'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'docs/**'
|
||||
- '.gitignore'
|
||||
- 'LICENSE'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: read # Allows SonarCloud to decorate PRs with analysis results
|
||||
security-events: write # Required for CodeQL analysis and uploading SARIF results
|
||||
|
||||
env:
|
||||
DOTNET_VERSION: '8.x'
|
||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: true
|
||||
|
||||
jobs:
|
||||
Code_Coverage:
|
||||
timeout-minutes: 30
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup .NET SDK
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
- name: Cache NuGet packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.nuget/packages
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
|
||||
restore-keys: ${{ runner.os }}-nuget-
|
||||
|
||||
- name: Cache dotnet tools
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.dotnet/tools
|
||||
key: ${{ runner.os }}-dotnet-tools-${{ hashFiles('**/*.csproj') }}
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'zulu'
|
||||
|
||||
- name: Cache SonarCloud scanner
|
||||
id: cache-sonar-scanner
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .\.sonar\scanner
|
||||
key: ${{ runner.os }}-sonar-scanner
|
||||
restore-keys: ${{ runner.os }}-sonar-scanner
|
||||
|
||||
- name: Install dotnet tools
|
||||
run: |
|
||||
dotnet tool install JetBrains.dotCover.GlobalTool --global
|
||||
dotnet tool install dotnet-sonarscanner --global
|
||||
dotnet tool install dotnet-coverage --global
|
||||
dotnet tool install --global coverlet.console
|
||||
dotnet tool install --global dotnet-reportgenerator-globaltool
|
||||
dotnet restore
|
||||
|
||||
- name: Begin SonarCloud Analysis
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
shell: powershell
|
||||
run: |
|
||||
dotnet sonarscanner begin /k:"mihakralj_QuanTAlib" /o:"mihakralj-quantalib" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" `
|
||||
/d:sonar.solution.file="QuanTAlib.sln" `
|
||||
/d:sonar.cs.opencover.reportsPaths="**/*cover*.xml" `
|
||||
/d:sonar.cs.dotcover.reportsPaths="**/dotcover.xml" `
|
||||
/d:sonar.coverage.exclusions="**Tests.cs,**/*.md,**/*.html,**/*.css,**/docs/**/*,**/archive/**/*,**/notebooks/**/*,**/obj/**/*,**/bin/**/*" `
|
||||
/d:sonar.exclusions="**/TestResults/**/*,**/bin/**/*,**/obj/**/*,**/*.html,**/coverage/**/*,**/CoverageReport/**/*,**/*.md,**/*.css,**/docs/**/*,**/archive/**/*,**/notebooks/**/*" `
|
||||
/d:sonar.test.exclusions="**Tests.cs,**/obj/**/*,**/bin/**/*" `
|
||||
/d:sonar.cpd.exclusions="**Tests.cs" `
|
||||
/d:sonar.scanner.scanAll="false" `
|
||||
/d:sonar.cs.roslyn.ignoreIssues="false" `
|
||||
/d:sonar.issue.ignore.multicriteria="e1" `
|
||||
/d:sonar.issue.ignore.multicriteria.e1.ruleKey="csharpsquid:S1944,csharpsquid:S2053,csharpsquid:S2222,csharpsquid:S2259,csharpsquid:S2583,csharpsquid:S2589,csharpsquid:S3329,csharpsquid:S3655,csharpsquid:S3900,csharpsquid:S3949,csharpsquid:S3966,csharpsquid:S4158,csharpsquid:S4347,csharpsquid:S5773,csharpsquid:S6781" `
|
||||
/d:sonar.issue.ignore.multicriteria.e1.resourceKey="**/*.cs" `
|
||||
/d:sonar.verbose="true"
|
||||
|
||||
- name: Build Projects
|
||||
id: build
|
||||
continue-on-error: true
|
||||
run: |
|
||||
dotnet build --no-restore --configuration Debug
|
||||
dotnet build ./lib/quantalib.csproj --configuration Release --nologo
|
||||
dotnet build ./quantower/Averages/_Averages.csproj --configuration Release --nologo
|
||||
dotnet build ./quantower/Statistics/_Statistics.csproj --configuration Release --nologo
|
||||
dotnet build ./quantower/Volatility/_Volatility.csproj --configuration Release --nologo
|
||||
dotnet build ./SyntheticVendor/SyntheticVendor.csproj --configuration Release --nologo
|
||||
if ($LASTEXITCODE -ne 0) { Write-Error "Build failed" }
|
||||
|
||||
- name: Check Build Status
|
||||
if: steps.build.outcome == 'failure'
|
||||
run: exit 1
|
||||
|
||||
- name: Run Tests with Coverage
|
||||
id: tests
|
||||
continue-on-error: true
|
||||
run: |
|
||||
dotnet test --no-build --configuration Debug /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
|
||||
dotnet-coverage collect "dotnet test" -f xml -o "coverage.xml"
|
||||
dotnet dotcover test Tests/Tests.csproj --dcReportType=HTML --dcoutput=./dotcover.html
|
||||
dotnet dotcover test Tests/Tests.csproj --dcReportType=DetailedXML --dcoutput=./dotcover.xml --verbosity=Detailed
|
||||
dotnet test -p:CollectCoverage=true --collect:"XPlat Code Coverage" --results-directory "./"
|
||||
|
||||
- name: Generate Coverage Report
|
||||
run: |
|
||||
reportgenerator -reports:*cover*.xml -targetdir:./coverage-report
|
||||
|
||||
- name: Upload Coverage Reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-reports
|
||||
path: |
|
||||
**/TestResults
|
||||
**/coverage-report
|
||||
**/*cover*.xml
|
||||
**/dotcover.*
|
||||
|
||||
- name: End SonarCloud Analysis
|
||||
if: always()
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
shell: powershell
|
||||
run: dotnet sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"
|
||||
|
||||
- name: Upload Coverage to Codacy
|
||||
uses: codacy/codacy-coverage-reporter-action@v1
|
||||
with:
|
||||
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
|
||||
coverage-reports: '*cover*.xml'
|
||||
|
||||
- name: Upload Coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: 'cover*'
|
||||
verbose: true
|
||||
|
||||
CodeQL:
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup .NET SDK
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
- name: Cache NuGet packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.nuget/packages
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
|
||||
restore-keys: ${{ runner.os }}-nuget-
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: 'csharp'
|
||||
queries: security-and-quality
|
||||
config-file: ./.github/codeql/codeql-config.yml
|
||||
tools: linked
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build
|
||||
run: dotnet build --no-restore --configuration Debug
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
output: results
|
||||
upload: true
|
||||
|
||||
- name: Run Snyk to check for vulnerabilities
|
||||
uses: snyk/actions/dotnet@master
|
||||
continue-on-error: true
|
||||
env:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
LD_PRELOAD: '' # Clear the LD_PRELOAD to avoid CodeQL conflicts
|
||||
with:
|
||||
args: |
|
||||
--file=./lib/quantalib.csproj
|
||||
--severity-threshold=low
|
||||
--detection-depth=4
|
||||
--package-manager=nuget
|
||||
|
||||
- name: Run Snyk on Solution
|
||||
uses: snyk/actions/dotnet@master
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
env:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
LD_PRELOAD: ''
|
||||
with:
|
||||
args: |
|
||||
--file=QuanTAlib.sln
|
||||
--all-projects
|
||||
--detection-depth=4
|
||||
|
||||
- name: Run Snyk IaC
|
||||
uses: snyk/actions/iac@master
|
||||
continue-on-error: true
|
||||
env:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
LD_PRELOAD: ''
|
||||
with:
|
||||
args: |
|
||||
--severity-threshold=low
|
||||
|
||||
build_publish:
|
||||
timeout-minutes: 20
|
||||
needs: [Code_Coverage, CodeQL]
|
||||
if: |
|
||||
success() &&
|
||||
(github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev')) ||
|
||||
github.event_name == 'workflow_dispatch'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup .NET SDK
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
- name: Install GitVersion
|
||||
uses: gittools/actions/gitversion/setup@v0
|
||||
with:
|
||||
versionSpec: '6.x'
|
||||
includePrerelease: true
|
||||
|
||||
- name: Determine Version
|
||||
id: gitversion
|
||||
uses: gittools/actions/gitversion/execute@v0
|
||||
with:
|
||||
useConfigFile: true
|
||||
updateAssemblyInfo: false
|
||||
|
||||
- name: Cache NuGet packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.nuget/packages
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
|
||||
restore-keys: ${{ runner.os }}-nuget-
|
||||
|
||||
- name: Build projects
|
||||
run: |
|
||||
dotnet restore
|
||||
dotnet build ./lib/quantalib.csproj --configuration Release --nologo
|
||||
dotnet build ./quantower/Averages/_Averages.csproj --configuration Release --nologo
|
||||
dotnet build ./quantower/Statistics/_Statistics.csproj --configuration Release --nologo
|
||||
dotnet build ./quantower/Volatility/_Volatility.csproj --configuration Release --nologo
|
||||
dotnet build ./SyntheticVendor/SyntheticVendor.csproj --configuration Release --nologo
|
||||
|
||||
- name: Create or Update Development Release
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh release delete development --yes || true
|
||||
gh release create development \
|
||||
--title "Development Build" \
|
||||
--notes "Latest development build from commit ${{ github.sha }}" \
|
||||
--prerelease \
|
||||
--target ${{ github.sha }} \
|
||||
lib/bin/Release/QuanTAlib.dll \
|
||||
quantower/Averages/bin/Release/Averages.dll \
|
||||
quantower/Statistics/bin/Release/Statistics.dll \
|
||||
quantower/Volatility/bin/Release/Volatility.dll \
|
||||
SyntheticVendor/bin/Release/SyntheticVendor.dll
|
||||
|
||||
- name: Push prerelease package to myget.org
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
continue-on-error: true
|
||||
id: myget-push
|
||||
run: |
|
||||
dotnet nuget push 'lib/bin/Release/QuanTAlib.*.nupkg' \
|
||||
--source https://www.myget.org/F/quantalib/api/v3/index.json \
|
||||
--force-english-output \
|
||||
--api-key ${{ secrets.MYGET_DEPLOY_KEY_QUANTALIB }}
|
||||
|
||||
- name: Create GitHub Release
|
||||
if: github.ref == 'refs/heads/main'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh release create v${{ steps.gitversion.outputs.MajorMinorPatch }} \
|
||||
--title "Release from commit ${{ steps.gitversion.outputs.MajorMinorPatch }}" \
|
||||
--notes "Release notes for this version." \
|
||||
quantower/Averages/bin/Release/Averages.dll \
|
||||
quantower/Statistics/bin/Release/Statistics.dll \
|
||||
quantower/Volatility/bin/Release/Volatility.dll \
|
||||
SyntheticVendor/bin/Release/SyntheticVendor.dll
|
||||
|
||||
- name: Push release package to nuget.org
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
run: |
|
||||
dotnet nuget push 'lib/bin/Release/QuanTAlib.*.nupkg' \
|
||||
--source https://api.nuget.org/v3/index.json \
|
||||
--skip-duplicate \
|
||||
--api-key ${{ secrets.NUGET_DEPLOY_KEY_QUANTLIB }}
|
||||
name: Publish Workflow
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'docs/**'
|
||||
- '.gitignore'
|
||||
- 'LICENSE'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'docs/**'
|
||||
- '.gitignore'
|
||||
- 'LICENSE'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: read # Allows SonarCloud to decorate PRs with analysis results
|
||||
security-events: write # Required for CodeQL analysis and uploading SARIF results
|
||||
|
||||
env:
|
||||
DOTNET_VERSION: '8.x'
|
||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: true
|
||||
|
||||
jobs:
|
||||
Code_Coverage:
|
||||
timeout-minutes: 30
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup .NET SDK
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
- name: Cache NuGet packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.nuget/packages
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
|
||||
restore-keys: ${{ runner.os }}-nuget-
|
||||
|
||||
- name: Cache dotnet tools
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.dotnet/tools
|
||||
key: ${{ runner.os }}-dotnet-tools-${{ hashFiles('**/*.csproj') }}
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'zulu'
|
||||
|
||||
- name: Cache SonarCloud scanner
|
||||
id: cache-sonar-scanner
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .\.sonar\scanner
|
||||
key: ${{ runner.os }}-sonar-scanner
|
||||
restore-keys: ${{ runner.os }}-sonar-scanner
|
||||
|
||||
- name: Install dotnet tools
|
||||
run: |
|
||||
dotnet tool install JetBrains.dotCover.GlobalTool --global
|
||||
dotnet tool install dotnet-sonarscanner --global
|
||||
dotnet tool install dotnet-coverage --global
|
||||
dotnet tool install --global coverlet.console
|
||||
dotnet tool install --global dotnet-reportgenerator-globaltool
|
||||
dotnet restore
|
||||
|
||||
- name: Begin SonarCloud Analysis
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
shell: powershell
|
||||
run: |
|
||||
dotnet sonarscanner begin /k:"mihakralj_QuanTAlib" /o:"mihakralj-quantalib" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" `
|
||||
/d:sonar.solution.file="QuanTAlib.sln" `
|
||||
/d:sonar.cs.opencover.reportsPaths="**/*cover*.xml" `
|
||||
/d:sonar.cs.dotcover.reportsPaths="**/dotcover.xml" `
|
||||
/d:sonar.coverage.exclusions="**Tests.cs,**/*.md,**/*.html,**/*.css,**/docs/**/*,**/archive/**/*,**/notebooks/**/*,**/obj/**/*,**/bin/**/*" `
|
||||
/d:sonar.exclusions="**/TestResults/**/*,**/bin/**/*,**/obj/**/*,**/*.html,**/coverage/**/*,**/CoverageReport/**/*,**/*.md,**/*.css,**/docs/**/*,**/archive/**/*,**/notebooks/**/*" `
|
||||
/d:sonar.test.exclusions="**Tests.cs,**/obj/**/*,**/bin/**/*" `
|
||||
/d:sonar.cpd.exclusions="**Tests.cs" `
|
||||
/d:sonar.scanner.scanAll="false" `
|
||||
/d:sonar.cs.roslyn.ignoreIssues="false" `
|
||||
/d:sonar.issue.ignore.multicriteria="e1" `
|
||||
/d:sonar.issue.ignore.multicriteria.e1.ruleKey="csharpsquid:S1944,csharpsquid:S2053,csharpsquid:S2222,csharpsquid:S2259,csharpsquid:S2583,csharpsquid:S2589,csharpsquid:S3329,csharpsquid:S3655,csharpsquid:S3900,csharpsquid:S3949,csharpsquid:S3966,csharpsquid:S4158,csharpsquid:S4347,csharpsquid:S5773,csharpsquid:S6781" `
|
||||
/d:sonar.issue.ignore.multicriteria.e1.resourceKey="**/*.cs" `
|
||||
/d:sonar.verbose="true"
|
||||
|
||||
- name: Build Projects
|
||||
id: build
|
||||
continue-on-error: true
|
||||
run: |
|
||||
dotnet build --no-restore --configuration Debug
|
||||
dotnet build ./lib/quantalib.csproj --configuration Release --nologo
|
||||
dotnet build ./quantower/Averages/_Averages.csproj --configuration Release --nologo
|
||||
dotnet build ./quantower/Statistics/_Statistics.csproj --configuration Release --nologo
|
||||
dotnet build ./quantower/Volatility/_Volatility.csproj --configuration Release --nologo
|
||||
dotnet build ./SyntheticVendor/SyntheticVendor.csproj --configuration Release --nologo
|
||||
if ($LASTEXITCODE -ne 0) { Write-Error "Build failed" }
|
||||
|
||||
- name: Check Build Status
|
||||
if: steps.build.outcome == 'failure'
|
||||
run: exit 1
|
||||
|
||||
- name: Run Tests with Coverage
|
||||
id: tests
|
||||
continue-on-error: true
|
||||
run: |
|
||||
dotnet test --no-build --configuration Debug /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
|
||||
dotnet-coverage collect "dotnet test" -f xml -o "coverage.xml"
|
||||
dotnet dotcover test Tests/Tests.csproj --dcReportType=HTML --dcoutput=./dotcover.html
|
||||
dotnet dotcover test Tests/Tests.csproj --dcReportType=DetailedXML --dcoutput=./dotcover.xml --verbosity=Detailed
|
||||
dotnet test -p:CollectCoverage=true --collect:"XPlat Code Coverage" --results-directory "./"
|
||||
|
||||
- name: Generate Coverage Report
|
||||
run: |
|
||||
reportgenerator -reports:*cover*.xml -targetdir:./coverage-report
|
||||
|
||||
- name: Upload Coverage Reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-reports
|
||||
path: |
|
||||
**/TestResults
|
||||
**/coverage-report
|
||||
**/*cover*.xml
|
||||
**/dotcover.*
|
||||
|
||||
- name: End SonarCloud Analysis
|
||||
if: always()
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
shell: powershell
|
||||
run: dotnet sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"
|
||||
|
||||
- name: Upload Coverage to Codacy
|
||||
uses: codacy/codacy-coverage-reporter-action@v1
|
||||
with:
|
||||
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
|
||||
coverage-reports: '*cover*.xml'
|
||||
|
||||
- name: Upload Coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
files: 'cover*'
|
||||
verbose: true
|
||||
|
||||
CodeQL:
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup .NET SDK
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
- name: Cache NuGet packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.nuget/packages
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
|
||||
restore-keys: ${{ runner.os }}-nuget-
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: 'csharp'
|
||||
queries: security-and-quality
|
||||
config-file: ./.github/codeql/codeql-config.yml
|
||||
tools: linked
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build
|
||||
run: dotnet build --no-restore --configuration Debug
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
output: results
|
||||
upload: true
|
||||
|
||||
- name: Run Snyk to check for vulnerabilities
|
||||
uses: snyk/actions/dotnet@master
|
||||
continue-on-error: true
|
||||
env:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
LD_PRELOAD: '' # Clear the LD_PRELOAD to avoid CodeQL conflicts
|
||||
with:
|
||||
args: |
|
||||
--file=./lib/quantalib.csproj
|
||||
--severity-threshold=low
|
||||
--detection-depth=4
|
||||
--package-manager=nuget
|
||||
|
||||
- name: Run Snyk on Solution
|
||||
uses: snyk/actions/dotnet@master
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
env:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
LD_PRELOAD: ''
|
||||
with:
|
||||
args: |
|
||||
--file=QuanTAlib.sln
|
||||
--all-projects
|
||||
--detection-depth=4
|
||||
|
||||
- name: Run Snyk IaC
|
||||
uses: snyk/actions/iac@master
|
||||
continue-on-error: true
|
||||
env:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
LD_PRELOAD: ''
|
||||
with:
|
||||
args: |
|
||||
--severity-threshold=low
|
||||
|
||||
build_publish:
|
||||
timeout-minutes: 20
|
||||
needs: [Code_Coverage, CodeQL]
|
||||
if: |
|
||||
success() &&
|
||||
(github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev')) ||
|
||||
github.event_name == 'workflow_dispatch'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup .NET SDK
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
- name: Install GitVersion
|
||||
uses: gittools/actions/gitversion/setup@v0
|
||||
with:
|
||||
versionSpec: '6.x'
|
||||
includePrerelease: true
|
||||
|
||||
- name: Determine Version
|
||||
id: gitversion
|
||||
uses: gittools/actions/gitversion/execute@v0
|
||||
with:
|
||||
useConfigFile: true
|
||||
updateAssemblyInfo: false
|
||||
|
||||
- name: Cache NuGet packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.nuget/packages
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
|
||||
restore-keys: ${{ runner.os }}-nuget-
|
||||
|
||||
- name: Build projects
|
||||
run: |
|
||||
dotnet restore
|
||||
dotnet build ./lib/quantalib.csproj --configuration Release --nologo
|
||||
dotnet build ./quantower/Averages/_Averages.csproj --configuration Release --nologo
|
||||
dotnet build ./quantower/Statistics/_Statistics.csproj --configuration Release --nologo
|
||||
dotnet build ./quantower/Volatility/_Volatility.csproj --configuration Release --nologo
|
||||
dotnet build ./SyntheticVendor/SyntheticVendor.csproj --configuration Release --nologo
|
||||
|
||||
- name: Create or Update Development Release
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh release delete development --yes || true
|
||||
gh release create development \
|
||||
--title "Development Build" \
|
||||
--notes "Latest development build from commit ${{ github.sha }}" \
|
||||
--prerelease \
|
||||
--target ${{ github.sha }} \
|
||||
lib/bin/Release/QuanTAlib.dll \
|
||||
quantower/Averages/bin/Release/Averages.dll \
|
||||
quantower/Statistics/bin/Release/Statistics.dll \
|
||||
quantower/Volatility/bin/Release/Volatility.dll \
|
||||
SyntheticVendor/bin/Release/SyntheticVendor.dll
|
||||
|
||||
- name: Push prerelease package to myget.org
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
continue-on-error: true
|
||||
id: myget-push
|
||||
run: |
|
||||
dotnet nuget push 'lib/bin/Release/QuanTAlib.*.nupkg' \
|
||||
--source https://www.myget.org/F/quantalib/api/v3/index.json \
|
||||
--force-english-output \
|
||||
--api-key ${{ secrets.MYGET_DEPLOY_KEY_QUANTALIB }}
|
||||
|
||||
- name: Create GitHub Release
|
||||
if: github.ref == 'refs/heads/main'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh release create v${{ steps.gitversion.outputs.MajorMinorPatch }} \
|
||||
--title "Release from commit ${{ steps.gitversion.outputs.MajorMinorPatch }}" \
|
||||
--notes "Release notes for this version." \
|
||||
quantower/Averages/bin/Release/Averages.dll \
|
||||
quantower/Statistics/bin/Release/Statistics.dll \
|
||||
quantower/Volatility/bin/Release/Volatility.dll \
|
||||
SyntheticVendor/bin/Release/SyntheticVendor.dll
|
||||
|
||||
- name: Push release package to nuget.org
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
run: |
|
||||
dotnet nuget push 'lib/bin/Release/QuanTAlib.*.nupkg' \
|
||||
--source https://api.nuget.org/v3/index.json \
|
||||
--skip-duplicate \
|
||||
--api-key ${{ secrets.NUGET_DEPLOY_KEY_QUANTLIB }}
|
||||
|
||||
Reference in New Issue
Block a user