# Interface Stability Contract - SDK ↔ Adapter Boundary **Version**: 1.0 **Date**: February 14, 2026 **Status**: DRAFT - Needs Review & Approval --- ## Purpose This document defines the **stable API contract** between: - **NT8.Core.dll** (SDK - platform-agnostic) - **NT8.Adapters.dll** (Adapters - platform-specific) **Goal**: Ensure SDK updates don't break adapters, and vice versa. --- ## Design Principles ### 1. **Stable Interfaces, Evolving Implementations** - Interfaces are contracts (change rarely) - Implementations can evolve freely (change often) - Adapters depend on interfaces, NOT implementations ### 2. **Semantic Versioning** - **MAJOR**: Breaking changes to interfaces - **MINOR**: New features (backward compatible) - **PATCH**: Bug fixes (no API changes) ### 3. **Explicit Deprecation Cycle** - Deprecated methods stay for 2+ minor versions - Mark with `[Obsolete]` attribute - Provide migration path in docs --- ## Current Interface Contract (v1.0) ### Core SDK Interfaces (STABLE) These interfaces define the boundary. **Changes require MAJOR version bump.** #### 1. Strategy Interface ```csharp // src/NT8.Core/Common/Interfaces/IStrategy.cs // VERSION: 1.0.0 // STABILITY: STABLE public interface IStrategy { // Properties StrategyMetadata Metadata { get; } // Methods void Initialize(StrategyConfig config, IMarketDataProvider dataProvider, ILogger logger); StrategyIntent OnBar(BarData bar, StrategyContext context); StrategyIntent OnTick(TickData tick, StrategyContext context); Dictionary GetParameters(); void SetParameters(Dictionary parameters); } ``` **Stability Guarantee:** - ✅ Method signatures won't change - ✅ Method names won't change - ✅ Return types won't change (may add properties to returned objects) - ✅ Parameter types won't change (may add optional parameters) **Allowed Changes:** - ➕ Add new optional methods with default implementations - ➕ Add properties to `StrategyMetadata` - ➕ Add properties to `StrategyConfig` - ❌ Cannot remove existing methods - ❌ Cannot change existing method signatures --- #### 2. Data Models (STABLE) ```csharp // src/NT8.Core/Common/Models/BarData.cs // VERSION: 1.0.0 // STABILITY: STABLE public class BarData { // Core properties (IMMUTABLE) public string Symbol { get; set; } public DateTime Time { get; set; } public double Open { get; set; } public double High { get; set; } public double Low { get; set; } public double Close { get; set; } public long Volume { get; set; } public TimeSpan BarSize { get; set; } // Constructor (IMMUTABLE signature) public BarData( string symbol, DateTime time, double open, double high, double low, double close, long volume, TimeSpan barSize) { ... } } ``` **Stability Guarantee:** - ✅ Existing properties keep same names - ✅ Existing properties keep same types - ✅ Constructor signature won't change (may add overloads) - ✅ Property getters/setters won't change behavior **Allowed Changes:** - ➕ Add new properties (with sensible defaults) - ➕ Add new constructor overloads - ➕ Add helper methods - ❌ Cannot rename existing properties - ❌ Cannot change property types - ❌ Cannot remove properties --- #### 3. Strategy Intent (STABLE) ```csharp // src/NT8.Core/Common/Models/StrategyIntent.cs // VERSION: 1.0.0 // STABILITY: STABLE public class StrategyIntent { // Core properties (IMMUTABLE) public string IntentId { get; private set; } public DateTime Timestamp { get; private set; } public string Symbol { get; set; } public OrderSide Side { get; set; } public OrderType EntryType { get; set; } public double? LimitPrice { get; set; } public int StopTicks { get; set; } public int? TargetTicks { get; set; } public double Confidence { get; set; } public string Reason { get; set; } public Dictionary Metadata { get; set; } // Constructor (IMMUTABLE signature) public StrategyIntent( string symbol, OrderSide side, OrderType entryType, double? limitPrice, int stopTicks, int? targetTicks, double confidence, string reason, Dictionary metadata) { ... } // Validation (IMMUTABLE signature) public bool IsValid() { ... } } ``` **Stability Guarantee:** - ✅ All property names fixed - ✅ All property types fixed - ✅ `IsValid()` signature fixed - ✅ Enums can add new values (but not remove) **Allowed Changes:** - ➕ Add new optional properties - ➕ Add new validation methods - ➕ Add enum values to `OrderSide`, `OrderType` - ❌ Cannot remove properties - ❌ Cannot change property types - ❌ Cannot remove enum values --- #### 4. Strategy Context (STABLE) ```csharp // src/NT8.Core/Common/Models/StrategyContext.cs // VERSION: 1.0.0 // STABILITY: STABLE public class StrategyContext { // Core properties (IMMUTABLE) public string Symbol { get; set; } public DateTime CurrentTime { get; set; } public Position CurrentPosition { get; set; } public AccountInfo Account { get; set; } public MarketSession Session { get; set; } public Dictionary CustomData { get; set; } // Constructor (IMMUTABLE signature) public StrategyContext( string symbol, DateTime currentTime, Position currentPosition, AccountInfo account, MarketSession session, Dictionary customData) { ... } } ``` **Stability Guarantee:** - ✅ Context structure is stable - ✅ Nested objects (`Position`, `AccountInfo`, `MarketSession`) can add properties - ✅ `CustomData` dictionary for extensibility **Allowed Changes:** - ➕ Add properties to `Position`, `AccountInfo`, `MarketSession` - ➕ Add new top-level properties to `StrategyContext` - ❌ Cannot remove existing properties - ❌ Cannot change property types --- ### Adapter Interface (SEMI-STABLE) This is platform-specific. Can evolve more freely. ```csharp // src/NT8.Adapters/NinjaTrader/INT8Adapter.cs // VERSION: 1.0.0 // STABILITY: SEMI-STABLE (can evolve between minor versions) public interface INT8Adapter { // Initialization void Initialize(IRiskManager riskManager, IPositionSizer positionSizer); // Data conversion (SDK models as return types = STABLE) BarData ConvertToSdkBar(string symbol, DateTime time, double open, double high, double low, double close, long volume, int barSize); AccountInfo ConvertToSdkAccount(double equity, double buyingPower, double dailyPnL, double maxDrawdown, DateTime lastUpdate); Position ConvertToSdkPosition(string symbol, int quantity, double averagePrice, double unrealizedPnL, double realizedPnL, DateTime lastUpdate); // Intent execution (SDK models as parameters = STABLE) void ExecuteIntent(StrategyIntent intent, SizingResult sizing); // NT8 callbacks (NT8-specific parameters = CAN CHANGE) void OnOrderUpdate(string orderId, double limitPrice, double stopPrice, int quantity, int filled, double averageFillPrice, string orderState, DateTime time, string errorCode, string nativeError); void OnExecutionUpdate(string executionId, string orderId, double price, int quantity, string marketPosition, DateTime time); } ``` **Stability Guarantee:** - ✅ Methods that return SDK models are stable - ✅ Methods that accept SDK models are stable - 🟡 NT8-specific signatures can evolve (minor versions) **Allowed Changes:** - ➕ Add new conversion methods - ➕ Add optional parameters to NT8-specific methods - ➕ Add new callback methods - ❌ Cannot change SDK model signatures - 🟡 Can change NT8-specific signatures (with deprecation) --- ## Versioning Strategy ### SDK Core (NT8.Core.dll) ``` Version Format: MAJOR.MINOR.PATCH 1.0.0 → Initial release (Phase 1) 1.1.0 → Add new optional features (Phase 2) 1.2.0 → More new features (Phase 3) 2.0.0 → Breaking interface changes (major refactor) ``` **Breaking Changes (MAJOR bump):** - Remove methods from `IStrategy` - Change method signatures in core interfaces - Remove properties from data models - Change property types in data models **Compatible Changes (MINOR bump):** - Add new optional interface methods (with defaults) - Add new properties to data models - Add new data models - Add new interfaces **Bug Fixes (PATCH bump):** - Fix bugs in implementations - Performance improvements - Internal refactoring - Documentation updates --- ### Adapters (NT8.Adapters.dll) ``` Version Format: Matches SDK version for compatibility NT8.Core 1.0.0 → NT8.Adapters 1.0.x (compatible) NT8.Core 1.1.0 → NT8.Adapters 1.1.x (compatible) NT8.Core 2.0.0 → NT8.Adapters 2.0.x (requires rewrite) ``` **Adapter versions MUST match SDK MAJOR.MINOR** --- ## Interface Evolution Examples ### Example 1: Adding Optional Feature (MINOR version) **Scenario**: Add support for multiple targets ```csharp // v1.0.0 - Original public class StrategyIntent { public int? TargetTicks { get; set; } // ... } // v1.1.0 - Enhanced (BACKWARD COMPATIBLE) public class StrategyIntent { public int? TargetTicks { get; set; } // Keep for compatibility // NEW: Optional multiple targets public List TargetTicksList { get; set; } // Defaults to null // Helper to maintain compatibility public int? GetPrimaryTarget() { if (TargetTicksList != null && TargetTicksList.Count > 0) return TargetTicksList[0]; return TargetTicks; } } ``` **Adapter Impact**: ✅ None - old code still works **Strategy Impact**: ✅ None - can optionally use new feature --- ### Example 2: Deprecating Old Method (MINOR version with warning) **Scenario**: Rename method for clarity ```csharp // v1.0.0 - Original public interface IStrategy { StrategyIntent OnBar(BarData bar, StrategyContext context); } // v1.1.0 - Add new, deprecate old public interface IStrategy { [Obsolete("Use OnBarClose instead. Will be removed in v2.0.0")] StrategyIntent OnBar(BarData bar, StrategyContext context); // NEW preferred method StrategyIntent OnBarClose(BarData bar, StrategyContext context); } // v1.2.0 - Still support both // ... same as v1.1.0 ... // v2.0.0 - Remove old (BREAKING CHANGE) public interface IStrategy { StrategyIntent OnBarClose(BarData bar, StrategyContext context); } ``` **Migration Timeline**: - v1.1.0: Add new method, deprecate old (warning) - v1.2.0: Keep both (warning) - v2.0.0: Remove old (breaking change, requires adapter update) --- ### Example 3: Breaking Change (MAJOR version) **Scenario**: Change position model to support multi-symbol ```csharp // v1.x.x - Original public class StrategyContext { public Position CurrentPosition { get; set; } // Single position } // v2.0.0 - Breaking change public class StrategyContext { public Dictionary Positions { get; set; } // Multi-symbol // Helper for single-symbol strategies public Position GetPosition(string symbol) { return Positions.ContainsKey(symbol) ? Positions[symbol] : null; } } ``` **Adapter Impact**: ❌ Must update - breaking change **Migration Required**: Yes, all adapters need rewrite **Version Bump**: 1.x.x → 2.0.0 --- ## Compatibility Matrix ### SDK ↔ Adapter Compatibility | SDK Version | Adapter Version | Compatible? | Notes | |-------------|-----------------|-------------|-------| | 1.0.0 | 1.0.x | ✅ Yes | Perfect match | | 1.1.0 | 1.0.x | ✅ Yes | Forward compatible | | 1.1.0 | 1.1.x | ✅ Yes | Perfect match | | 1.2.0 | 1.1.x | ✅ Yes | Forward compatible | | 2.0.0 | 1.x.x | ❌ No | Breaking change | | 2.0.0 | 2.0.x | ✅ Yes | Perfect match | **Rule**: Adapter MINOR can be less than SDK MINOR (forward compatible) **Rule**: Adapter MAJOR must equal SDK MAJOR --- ## Contract Testing ### Automated Contract Tests Create tests that verify interface stability: ```csharp // tests/NT8.Core.Tests/Contracts/InterfaceStabilityTests.cs [Fact] public void IStrategy_Interface_Should_Be_Stable() { var type = typeof(IStrategy); // Verify method count hasn't decreased var methods = type.GetMethods(); Assert.True(methods.Length >= 5, "IStrategy methods removed!"); // Verify specific methods exist Assert.NotNull(type.GetMethod("OnBar")); Assert.NotNull(type.GetMethod("OnTick")); Assert.NotNull(type.GetMethod("Initialize")); // Verify method signatures var onBarMethod = type.GetMethod("OnBar"); Assert.Equal(typeof(StrategyIntent), onBarMethod.ReturnType); var parameters = onBarMethod.GetParameters(); Assert.Equal(2, parameters.Length); Assert.Equal(typeof(BarData), parameters[0].ParameterType); Assert.Equal(typeof(StrategyContext), parameters[1].ParameterType); } [Fact] public void BarData_Properties_Should_Be_Stable() { var type = typeof(BarData); // Verify required properties exist Assert.NotNull(type.GetProperty("Symbol")); Assert.NotNull(type.GetProperty("Time")); Assert.NotNull(type.GetProperty("Open")); Assert.NotNull(type.GetProperty("High")); Assert.NotNull(type.GetProperty("Low")); Assert.NotNull(type.GetProperty("Close")); Assert.NotNull(type.GetProperty("Volume")); // Verify property types haven't changed Assert.Equal(typeof(string), type.GetProperty("Symbol").PropertyType); Assert.Equal(typeof(DateTime), type.GetProperty("Time").PropertyType); Assert.Equal(typeof(double), type.GetProperty("Open").PropertyType); } [Fact] public void StrategyIntent_Constructor_Should_Be_Stable() { var type = typeof(StrategyIntent); // Verify constructor exists with expected parameters var constructor = type.GetConstructor(new[] { typeof(string), // symbol typeof(OrderSide), // side typeof(OrderType), // entryType typeof(double?), // limitPrice typeof(int), // stopTicks typeof(int?), // targetTicks typeof(double), // confidence typeof(string), // reason typeof(Dictionary) // metadata }); Assert.NotNull(constructor); } ``` **Run these tests** in CI/CD to catch accidental breaking changes! --- ## Adapter-Safe Practices ### For SDK Developers **DO:** - ✅ Add optional parameters with defaults - ✅ Add new properties with sensible defaults - ✅ Add new interfaces for new features - ✅ Add helper methods to existing classes - ✅ Mark deprecated methods with `[Obsolete]` - ✅ Run contract tests before release **DON'T:** - ❌ Remove existing methods - ❌ Change method signatures - ❌ Rename properties - ❌ Change property types - ❌ Remove enum values - ❌ Break existing constructors ### For Adapter Developers **DO:** - ✅ Depend only on interfaces, not implementations - ✅ Use factory patterns for object creation - ✅ Handle new optional properties gracefully - ✅ Catch and log unknown enum values - ✅ Version-check SDK at runtime if needed **DON'T:** - ❌ Depend on internal implementation details - ❌ Assume fixed property counts - ❌ Hard-code enum values - ❌ Access private members via reflection --- ## Naming Conventions (IMMUTABLE) These naming patterns are **contracts** and won't change: ### Interface Names ``` Pattern: I{Concept} Examples: IStrategy, IRiskManager, IPositionSizer, ILogger ``` ### Model Classes ``` Pattern: {Concept}{Type} Examples: BarData, TickData, StrategyIntent, StrategyContext ``` ### Enums ``` Pattern: {Concept}{Optional Suffix} Examples: OrderSide, OrderType, TickType, RiskLevel ``` ### Methods ``` Pattern: {Verb}{Noun} Examples: OnBar, OnTick, Initialize, ValidateOrder, CalculateSize ``` ### Properties ``` Pattern: {Noun} or {Adjective}{Noun} Examples: Symbol, Timestamp, CurrentPosition, DailyPnL ``` **These patterns are STABLE and won't change.** --- ## Extension Points (For Future Growth) ### 1. Metadata Dictionaries ```csharp public Dictionary Metadata { get; set; } ``` **Use for**: Adding data without breaking compatibility ### 2. Optional Parameters ```csharp public void Initialize( StrategyConfig config, IMarketDataProvider dataProvider, ILogger logger, Dictionary options = null) // Extension point ``` ### 3. Interface Composition ```csharp // Instead of changing IStrategy, create new interface public interface IAdvancedStrategy : IStrategy { void OnMarketDepth(MarketDepthData depth); } ``` ### 4. Feature Flags ```csharp public class StrategyMetadata { public Dictionary SupportedFeatures { get; set; } } ``` **Adapters can check**: `if (metadata.SupportedFeatures["MultiTarget"]) { ... }` --- ## Documentation Requirements ### For Every Interface Change **Must Document:** 1. **What changed** (method/property added/removed/changed) 2. **Why it changed** (business reason) 3. **Version** it changed in 4. **Migration path** for adapters 5. **Deprecation timeline** (if applicable) **Example**: ```markdown ## v1.1.0 - January 2026 ### Added - `StrategyIntent.TargetTicksList` - Support for multiple profit targets - **Migration**: Not required. Old code using `TargetTicks` still works. - **New feature**: Strategies can now specify multiple targets. ### Deprecated - `IStrategy.OnBar()` - Deprecated in favor of `OnBarClose()` - **Reason**: Clearer naming for bar-close strategies - **Timeline**: Will be removed in v2.0.0 (12+ months) - **Migration**: Replace `OnBar` with `OnBarClose` (same signature) ``` --- ## Approval Process ### Before Releasing Interface Changes **Required Reviews:** 1. ✅ Technical review (breaking vs. non-breaking) 2. ✅ Contract tests pass 3. ✅ Documentation updated 4. ✅ Migration guide written (if needed) 5. ✅ Version number updated correctly **For Breaking Changes:** 1. ✅ All of the above, plus: 2. ✅ Deprecation period completed (2+ minor versions) 3. ✅ Adapter developers notified 30+ days in advance 4. ✅ Migration tooling provided (if possible) --- ## Summary ### The Contract **SDK Core provides:** - Stable interfaces (`IStrategy`, `IRiskManager`, `IPositionSizer`) - Stable data models (`BarData`, `StrategyIntent`, `StrategyContext`) - Versioned API (semantic versioning) - Backward compatibility within MAJOR versions **Adapters rely on:** - Interface contracts (not implementations) - Data model structures - Method signatures - Enum values **The promise:** - SDK can evolve WITHOUT breaking adapters (within MAJOR version) - Adapters can evolve WITHOUT rewriting SDK - Clear versioning communicates compatibility - Deprecation gives time to migrate --- ## Action Items ### Immediate (Before Phase 1 Release) - [ ] Review all public interfaces for stability - [ ] Add contract tests to CI/CD - [ ] Document current interface versions - [ ] Establish version numbering (start at 1.0.0) - [ ] Get team approval on this contract ### Ongoing - [ ] Run contract tests on every build - [ ] Review all PR's for interface stability - [ ] Document changes in CHANGELOG.md - [ ] Notify adapter developers of deprecations - [ ] Maintain compatibility matrix --- **Version History:** - v1.0 (2026-02-14): Initial draft - [Future versions will be listed here]