# Configuration Export/Import - Implementation Specification **For:** Kilocode AI Agent **Priority:** HIGH **Mode:** Code Mode **Estimated Time:** 1.5-2 hours **Files to Edit:** 1 file (NT8StrategyBase.cs) **Files to Create:** 1 file (StrategyConfigExporter.cs) --- ## 🎯 Objective Add ability to export NT8 strategy configuration as JSON for: - Easy sharing with support/debugging - Version control of strategy settings - Configuration backup/restore - Reproducible backtests --- ## 📋 What We're Adding ### 1. Export Configuration Button/Method User can click a button (or call a method) to export all strategy settings as JSON file. ### 2. Import Configuration Method User can load settings from a previously exported JSON file. ### 3. Automatic Export on Strategy Start Optionally auto-export config to a timestamped file when strategy starts. --- ## 🔧 Implementation ### Component 1: StrategyConfigExporter.cs **Location:** `src/NT8.Adapters/Strategies/StrategyConfigExporter.cs` **Purpose:** Static helper class to serialize/deserialize strategy configurations ```csharp using System; using System.Collections.Generic; using System.IO; using System.Text; namespace NT8.Adapters.Strategies { /// /// Helper class to export/import NT8 strategy configurations as JSON. /// Enables configuration sharing, backup, and reproducible testing. /// public static class StrategyConfigExporter { /// /// Export strategy configuration to JSON string. /// public static string ExportToJson(Dictionary config) { if (config == null || config.Count == 0) return "{}"; var sb = new StringBuilder(); sb.AppendLine("{"); var first = true; foreach (var kvp in config) { if (!first) sb.AppendLine(","); first = false; sb.Append(" \""); sb.Append(EscapeJsonString(kvp.Key)); sb.Append("\": "); AppendValue(sb, kvp.Value); } sb.AppendLine(); sb.Append("}"); return sb.ToString(); } /// /// Save configuration to JSON file. /// public static void ExportToFile(Dictionary config, string filepath) { var json = ExportToJson(config); File.WriteAllText(filepath, json); } /// /// Import configuration from JSON string. /// Simple parser for basic types (string, int, double, bool). /// public static Dictionary ImportFromJson(string json) { var config = new Dictionary(); if (string.IsNullOrWhiteSpace(json)) return config; // Remove outer braces and whitespace json = json.Trim(); if (json.StartsWith("{")) json = json.Substring(1); if (json.EndsWith("}")) json = json.Substring(0, json.Length - 1); // Split by commas (simple parser - doesn't handle nested objects) var lines = json.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (var line in lines) { var colonIndex = line.IndexOf(':'); if (colonIndex < 0) continue; var key = line.Substring(0, colonIndex).Trim().Trim('"'); var valueStr = line.Substring(colonIndex + 1).Trim(); var value = ParseValue(valueStr); config[key] = value; } return config; } /// /// Import configuration from JSON file. /// public static Dictionary ImportFromFile(string filepath) { if (!File.Exists(filepath)) throw new FileNotFoundException("Config file not found", filepath); var json = File.ReadAllText(filepath); return ImportFromJson(json); } #region Helper Methods private static void AppendValue(StringBuilder sb, object value) { if (value == null) { sb.Append("null"); } else if (value is string) { sb.Append("\""); sb.Append(EscapeJsonString(value.ToString())); sb.Append("\""); } else if (value is bool) { sb.Append(((bool)value) ? "true" : "false"); } else if (value is int || value is long || value is double || value is decimal || value is float) { sb.Append(value.ToString()); } else if (value is DateTime) { sb.Append("\""); sb.Append(((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss")); sb.Append("\""); } else { // Fallback: ToString() sb.Append("\""); sb.Append(EscapeJsonString(value.ToString())); sb.Append("\""); } } private static string EscapeJsonString(string str) { if (string.IsNullOrEmpty(str)) return str; return str .Replace("\\", "\\\\") .Replace("\"", "\\\"") .Replace("\n", "\\n") .Replace("\r", "\\r") .Replace("\t", "\\t"); } private static object ParseValue(string valueStr) { valueStr = valueStr.Trim(); // Remove trailing comma if present if (valueStr.EndsWith(",")) valueStr = valueStr.Substring(0, valueStr.Length - 1).Trim(); // Null if (valueStr == "null") return null; // Boolean if (valueStr == "true") return true; if (valueStr == "false") return false; // String (quoted) if (valueStr.StartsWith("\"") && valueStr.EndsWith("\"")) { var str = valueStr.Substring(1, valueStr.Length - 2); return UnescapeJsonString(str); } // Number - try int, then double int intVal; if (int.TryParse(valueStr, out intVal)) return intVal; double doubleVal; if (double.TryParse(valueStr, out doubleVal)) return doubleVal; // Fallback: return as string return valueStr; } private static string UnescapeJsonString(string str) { if (string.IsNullOrEmpty(str)) return str; return str .Replace("\\\"", "\"") .Replace("\\\\", "\\") .Replace("\\n", "\n") .Replace("\\r", "\r") .Replace("\\t", "\t"); } #endregion } } ``` --- ### Component 2: Add Export Methods to NT8StrategyBase.cs **File:** `src/NT8.Adapters/Strategies/NT8StrategyBase.cs` **Add these properties and methods:** ```csharp #region Configuration Export/Import [NinjaScriptProperty] [Display(Name = "Auto Export Config", GroupName = "SDK", Order = 10)] public bool AutoExportConfig { get; set; } [NinjaScriptProperty] [Display(Name = "Config Export Path", GroupName = "SDK", Order = 11)] public string ConfigExportPath { get; set; } /// /// Export current strategy configuration to JSON string. /// Can be called from derived strategies or used for debugging. /// public string ExportConfigurationJson() { var config = new Dictionary(); // Basic info config["StrategyName"] = Name; config["ExportedAt"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); config["Instrument"] = Instrument != null ? Instrument.FullName : "Not Set"; config["BarsPeriod"] = BarsPeriod != null ? BarsPeriod.ToString() : "Not Set"; // SDK settings config["EnableSDK"] = EnableSDK; config["AutoExportConfig"] = AutoExportConfig; config["ConfigExportPath"] = ConfigExportPath ?? ""; // Risk settings config["DailyLossLimit"] = DailyLossLimit; config["MaxTradeRisk"] = MaxTradeRisk; config["MaxOpenPositions"] = MaxOpenPositions; // Sizing settings config["RiskPerTrade"] = RiskPerTrade; config["MinContracts"] = MinContracts; config["MaxContracts"] = MaxContracts; // NT8 settings config["BarsRequiredToTrade"] = BarsRequiredToTrade; config["Calculate"] = Calculate.ToString(); config["EntriesPerDirection"] = EntriesPerDirection; config["StartBehavior"] = StartBehavior.ToString(); return StrategyConfigExporter.ExportToJson(config); } /// /// Export configuration to file. /// public void ExportConfigurationToFile(string filepath) { var config = GetConfigurationDictionary(); StrategyConfigExporter.ExportToFile(config, filepath); Print(string.Format("[SDK] Configuration exported to: {0}", filepath)); } /// /// Get configuration as dictionary for export. /// protected Dictionary GetConfigurationDictionary() { var config = new Dictionary(); config["StrategyName"] = Name; config["ExportedAt"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); config["Instrument"] = Instrument != null ? Instrument.FullName : "Not Set"; config["BarsPeriod"] = BarsPeriod != null ? BarsPeriod.ToString() : "Not Set"; config["EnableSDK"] = EnableSDK; config["AutoExportConfig"] = AutoExportConfig; config["ConfigExportPath"] = ConfigExportPath ?? ""; config["DailyLossLimit"] = DailyLossLimit; config["MaxTradeRisk"] = MaxTradeRisk; config["MaxOpenPositions"] = MaxOpenPositions; config["RiskPerTrade"] = RiskPerTrade; config["MinContracts"] = MinContracts; config["MaxContracts"] = MaxContracts; config["BarsRequiredToTrade"] = BarsRequiredToTrade; config["Calculate"] = Calculate.ToString(); config["EntriesPerDirection"] = EntriesPerDirection; config["StartBehavior"] = StartBehavior.ToString(); return config; } /// /// Print configuration to Output window for easy copy/paste. /// public void PrintConfiguration() { var json = ExportConfigurationJson(); Print("=== Strategy Configuration ==="); Print(json); Print("=== End Configuration ==="); } #endregion ``` **Update OnStateChange() to handle auto-export:** Find the `State.DataLoaded` section and add auto-export: ```csharp else if (State == State.DataLoaded) { if (EnableSDK) { try { InitializeSdkComponents(); _sdkInitialized = true; Print(string.Format("[SDK] {0} initialized successfully", Name)); // Auto-export configuration if enabled if (AutoExportConfig) { var exportPath = ConfigExportPath; // Default path if not specified if (string.IsNullOrEmpty(exportPath)) { var timestamp = DateTime.Now.ToString("yyyyMMdd-HHmmss"); var filename = string.Format("{0}_{1}_config.json", Name, timestamp); exportPath = System.IO.Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "NinjaTrader 8", "logs", filename ); } try { ExportConfigurationToFile(exportPath); } catch (Exception ex) { Print(string.Format("[SDK] Failed to export config: {0}", ex.Message)); } } // Print config to Output window for easy access PrintConfiguration(); } catch (Exception ex) { Print(string.Format("[SDK ERROR] Initialization failed: {0}", ex.Message)); Log(string.Format("[SDK ERROR] {0}", ex.ToString()), LogLevel.Error); _sdkInitialized = false; } } } ``` **Update State.SetDefaults to include new properties:** ```csharp if (State == State.SetDefaults) { // ... existing code ... // SDK configuration export AutoExportConfig = false; // Off by default ConfigExportPath = ""; // Empty = auto-generate path } ``` --- ### Component 3: Update SimpleORBNT8.cs **Add SimpleORB-specific configuration export:** ```csharp /// /// Export SimpleORB-specific configuration. /// Overrides base to include ORB parameters. /// protected new Dictionary GetConfigurationDictionary() { var config = base.GetConfigurationDictionary(); // Add ORB-specific settings config["OpeningRangeMinutes"] = OpeningRangeMinutes; config["StdDevMultiplier"] = StdDevMultiplier; config["StopTicks"] = StopTicks; config["TargetTicks"] = TargetTicks; return config; } ``` --- ## ✅ Verification ### Manual Test 1. **Build:** ```bash dotnet build src\NT8.Adapters\NT8.Adapters.csproj --configuration Release ``` 2. **Deploy:** ```powershell .\deployment\Deploy-To-NT8.ps1 ``` 3. **Test in NT8:** - Add SimpleORBNT8 to Strategy Analyzer - Enable strategy - Check Output window - should see: ``` [SDK] Simple ORB NT8 initialized successfully === Strategy Configuration === { "StrategyName": "Simple ORB NT8", "ExportedAt": "2026-02-17 14:30:00", "Instrument": "ES 03-26", "BarsPeriod": "5 Minute", "EnableSDK": true, "DailyLossLimit": 1000, ... } === End Configuration === ``` 4. **Copy JSON from Output window** - ready to share! 5. **Test Auto-Export:** - Set `AutoExportConfig = true` - Re-enable strategy - Check `Documents\NinjaTrader 8\logs\` folder - Should see `SimpleORBNT8_[timestamp]_config.json` --- ## 📋 Success Criteria - [ ] StrategyConfigExporter.cs created - [ ] Export methods added to NT8StrategyBase - [ ] Auto-export on strategy start works - [ ] PrintConfiguration() shows JSON in Output window - [ ] SimpleORBNT8 includes ORB-specific parameters - [ ] JSON format is valid and readable - [ ] Zero compilation errors - [ ] All 319 existing tests still pass --- ## 🚨 Constraints - C# 5.0 syntax only (no modern JSON libraries) - Simple manual JSON serialization (no Newtonsoft.Json dependency) - Thread-safe (no async file I/O) - Minimal allocations - Clear error messages --- ## 📋 Git Commit ```bash git add src/NT8.Adapters/Strategies/StrategyConfigExporter.cs git add src/NT8.Adapters/Strategies/NT8StrategyBase.cs git add src/NT8.Adapters/Strategies/SimpleORBNT8.cs git commit -m "feat: Add configuration export/import - Add StrategyConfigExporter helper class - Add ExportConfigurationJson() method - Add PrintConfiguration() to Output window - Add auto-export on strategy start - Add AutoExportConfig property - Simple JSON serialization (C# 5.0 compatible) Enables easy configuration sharing for debugging" ``` --- **READY FOR KILOCODE - CODE MODE** ✅ **Time: 1.5-2 hours**