mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-07-27 17:27:43 +00:00
fd6c80e8db
- Implemented CMF indicator in Cmf.cs with detailed calculations and methods. - Created unit tests for CMF validation against Skender, Ooples, and batch processing. - Added documentation for CMF in Cmf.md, explaining its purpose, calculations, and usage. - Updated project files to include new statistics library. - Updated NDepend badges to reflect changes in classes, methods, and lines of code.
227 lines
7.3 KiB
Markdown
227 lines
7.3 KiB
Markdown
# Integration Guides
|
||
|
||
QuanTAlib is platform-agnostic. Any .NET environment that can reference a DLL can use it. The complexity lies not in the library but in understanding each platform's quirks.
|
||
|
||
## Quantower
|
||
|
||
Quantower accepts custom indicators written in C#. Integration follows a wrapper pattern.
|
||
|
||
### Setup
|
||
|
||
1. Build QuanTAlib or grab the NuGet package
|
||
2. Add reference to `QuanTAlib.dll` in the Quantower indicator project
|
||
3. Create wrapper class inheriting from `Indicator`
|
||
|
||
### Example: SMA Indicator
|
||
|
||
```csharp
|
||
using Quantower.API.Indicators;
|
||
using QuanTAlib;
|
||
|
||
public class MySmaIndicator : Indicator
|
||
{
|
||
private Sma _sma;
|
||
|
||
[InputParameter("Period", 10, 1000, 1, 0)]
|
||
public int Period = 14;
|
||
|
||
public override void OnInit()
|
||
{
|
||
_sma = new Sma(Period);
|
||
AddLineSeries("SMA", Color.Yellow, LineStyle.Solid, 2);
|
||
}
|
||
|
||
public override void OnUpdate(UpdateArgs args)
|
||
{
|
||
double price = ClosePrice;
|
||
|
||
// Quantower handles bar lifecycle; check UpdateReason
|
||
bool isNew = args.Reason == UpdateReason.NewBar;
|
||
var result = _sma.Update(new TValue(DateTime.UtcNow, price), isNew);
|
||
|
||
SetValue(result.Value);
|
||
}
|
||
}
|
||
```
|
||
|
||
### Available Quantower Bundles
|
||
|
||
Pre-built adapters exist for common indicators:
|
||
|
||
| Bundle | Indicators | Notes |
|
||
| :----- | :--------- | :---- |
|
||
| Trends (IIR) | HemaIndicator, ZlemaIndicator, EmaIndicator, etc. | Exponential family |
|
||
| Trends (FIR) | SmaIndicator, WmaIndicator, HmaIndicator, etc. | Finite response family |
|
||
| Volatility | AtrIndicator, AdrIndicator | Range-based volatility |
|
||
| Dynamics | AdxIndicator, SuperTrendIndicator | Trend strength |
|
||
|
||
### Quantower Gotchas
|
||
|
||
**UpdateReason matters.** Quantower calls `OnUpdate` for both new bars and intra-bar ticks. The `args.Reason` check determines `isNew` flag behavior. Getting this wrong causes state corruption that manifests as mysteriously wrong indicator values.
|
||
|
||
**Historical data loads first.** Quantower calls `OnUpdate` repeatedly during historical load before live data arrives. The indicator warms up during this phase.
|
||
|
||
## NinjaTrader 8
|
||
|
||
NinjaTrader 8 runs on .NET Framework 4.8. QuanTAlib targets .NET Standard, enabling interop.
|
||
|
||
### Setup
|
||
|
||
1. Copy `QuanTAlib.dll` to `Documents\NinjaTrader 8\bin\Custom`
|
||
2. In NinjaScript Editor: right-click ’ References ’ Add `QuanTAlib.dll`
|
||
|
||
### Example: SMA Indicator
|
||
|
||
```csharp
|
||
private QuanTAlib.Sma _sma;
|
||
|
||
[Range(1, int.MaxValue)]
|
||
[NinjaScriptProperty]
|
||
public int Period { get; set; } = 14;
|
||
|
||
protected override void OnStateChange()
|
||
{
|
||
if (State == State.SetDefaults)
|
||
{
|
||
Name = "QuanTAlib SMA";
|
||
Calculate = Calculate.OnBarClose;
|
||
}
|
||
else if (State == State.DataLoaded)
|
||
{
|
||
_sma = new QuanTAlib.Sma(Period);
|
||
}
|
||
}
|
||
|
||
protected override void OnBarUpdate()
|
||
{
|
||
// isNew depends on Calculate mode
|
||
// OnBarClose: every call is a new bar
|
||
// OnEachTick: use IsFirstTickOfBar
|
||
bool isNew = Calculate == Calculate.OnBarClose || IsFirstTickOfBar;
|
||
|
||
var result = _sma.Update(new TValue(Time[0], Close[0]), isNew);
|
||
Value[0] = result.Value;
|
||
}
|
||
```
|
||
|
||
### NinjaTrader Gotchas
|
||
|
||
**Calculate mode affects isNew logic.** With `Calculate.OnBarClose`, every `OnBarUpdate` call represents a completed bar. With `Calculate.OnEachTick`, only the first tick of each bar should use `isNew = true`. Mixing these concepts produces indicators that work in backtest but fail live.
|
||
|
||
**Historical vs real-time.** NinjaTrader processes historical bars differently from real-time bars. The `State` property indicates the current phase. Indicator warmup should complete during historical processing.
|
||
|
||
## QuantConnect (LEAN)
|
||
|
||
LEAN supports custom libraries through NuGet integration.
|
||
|
||
### Setup
|
||
|
||
1. Add `QuanTAlib` to project dependencies
|
||
2. Instantiate indicators in `Initialize()`
|
||
3. Update in `OnData()`
|
||
|
||
### Example: Algorithm with SMA
|
||
|
||
```csharp
|
||
public class MyAlgorithm : QCAlgorithm
|
||
{
|
||
private Sma _mySma;
|
||
private Symbol _symbol;
|
||
|
||
public override void Initialize()
|
||
{
|
||
SetStartDate(2020, 1, 1);
|
||
SetEndDate(2023, 12, 31);
|
||
SetCash(100000);
|
||
|
||
_symbol = AddEquity("SPY", Resolution.Daily).Symbol;
|
||
_mySma = new Sma(14);
|
||
}
|
||
|
||
public override void OnData(Slice data)
|
||
{
|
||
if (!data.Bars.ContainsKey(_symbol))
|
||
return;
|
||
|
||
var bar = data.Bars[_symbol];
|
||
var result = _mySma.Update(new TValue(bar.EndTime, (double)bar.Close));
|
||
|
||
if (_mySma.IsHot)
|
||
{
|
||
Plot("Indicators", "SMA", result.Value);
|
||
|
||
// Trading logic here
|
||
if (!Portfolio[_symbol].Invested && result.Value < (double)bar.Close)
|
||
{
|
||
SetHoldings(_symbol, 0.5);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### LEAN Gotchas
|
||
|
||
**Decimal to double conversion.** LEAN uses `decimal` for prices; QuanTAlib uses `double`. Cast on input, cast back on output if needed. The precision difference rarely matters for indicator calculations.
|
||
|
||
**Resolution affects bar timing.** Daily bars have different `EndTime` semantics than minute bars. UTC timestamps prevent timezone confusion.
|
||
|
||
## Custom Platform Integration
|
||
|
||
For proprietary trading engines, Streaming Mode fits most use cases.
|
||
|
||
### Integration Checklist
|
||
|
||
| Consideration | Requirement | Consequence of Ignoring |
|
||
| :------------ | :---------- | :---------------------- |
|
||
| **Time handling** | UTC timestamps | Timezone bugs in historical analysis |
|
||
| **Numeric precision** | `double` input/output | Cast from/to `decimal` if platform uses it |
|
||
| **State persistence** | One instance per symbol | Recreating indicators loses warmup state |
|
||
| **Thread safety** | Separate instances per thread | Concurrent access corrupts internal state |
|
||
| **Bar correction** | Proper `isNew` flag usage | State accumulation errors |
|
||
|
||
### Minimal Integration Pattern
|
||
|
||
```csharp
|
||
public class MyTradingEngine
|
||
{
|
||
// One indicator instance per symbol, persisted for session lifetime
|
||
private readonly Dictionary<string, Sma> _indicators = new();
|
||
|
||
public void OnSymbolAdded(string symbol, int smaPeriod)
|
||
{
|
||
_indicators[symbol] = new Sma(smaPeriod);
|
||
}
|
||
|
||
public void OnTick(string symbol, DateTime time, double price, bool isNewBar)
|
||
{
|
||
if (!_indicators.TryGetValue(symbol, out var sma))
|
||
return;
|
||
|
||
var result = sma.Update(new TValue(time, price), isNewBar);
|
||
|
||
if (sma.IsHot)
|
||
{
|
||
// Use result.Value for trading logic
|
||
ProcessSignal(symbol, result.Value, price);
|
||
}
|
||
}
|
||
|
||
public void OnSymbolRemoved(string symbol)
|
||
{
|
||
_indicators.Remove(symbol);
|
||
}
|
||
}
|
||
```
|
||
|
||
### The isNew Flag: Getting It Right
|
||
|
||
The `isNew` flag determines whether `Update()` advances to the next bar or corrects the current one. Correct implementation depends on data source semantics:
|
||
|
||
| Data Source Type | isNew = true When | isNew = false When |
|
||
| :--------------- | :---------------- | :----------------- |
|
||
| Bar-based feed | New bar arrives | Never (each bar final) |
|
||
| Tick-based, bar aggregation | First tick after bar close | Subsequent ticks within bar |
|
||
| Streaming with corrections | Timestamp advances | Same timestamp, updated price |
|
||
|
||
**Testing approach:** Feed identical data through the indicator in streaming mode (tick by tick with correct `isNew` flags) and batch mode (complete series at once). Compare final values. Mismatch indicates `isNew` flag logic error. |