[SOLVED, kind of...] How to save Inputs to file

Questions about MultiCharts .NET and user contributed studies.
Talky_Zebra
Posts: 45
Joined: 07 Mar 2024
Has thanked: 13 times
Been thanked: 1 time

[SOLVED, kind of...] How to save Inputs to file

Postby Talky_Zebra » 21 Mar 2024

Hi all,

As a matter of learning both MC and C# I have been trying to save the Inputs to a file. This is so that I can change the inputs, and even remove the study from the chart, but be able to apply optimized/changed inputs from a file when applied to a new chart - especially for inputs that change from symbol to symbol.

Problems:
  • The serializers I have tried want an object with no parameters, and since MC studies have the _ctx parameter this is not an option.
  • I have also tried writing the InputAttributes to a file. But, while each of the fields properly shows in the Output (console), only strings write to a file... no matter how I convert them (x.ToString(), Convert.ToString(x), etc etc)
There's got to be a method for this. How is this done in MC?
Last edited by Talky_Zebra on 22 Mar 2024, edited 2 times in total.

Talky_Zebra
Posts: 45
Joined: 07 Mar 2024
Has thanked: 13 times
Been thanked: 1 time

Re: How to save Inputs to file

Postby Talky_Zebra » 22 Mar 2024

I knocked my head against this a bit more. It isn't as elegant as I would like it to be, but it is workable.

I am left with a question for the MultiCharts staff and the experienced coders on this forum:
When the signal or indicator class' constructor is called, as you insert the study - is there any way to collect the symbol info and timeframe resolution BEFORE you hit "OK" on the inputs box or "close" the signal study dialog? I would love to be able to use the symbol name and timeframe res as part of the folder/settings path. I have not been able to find a way to do this.

But, short of that, for those interested: below, find an example of the code needed to collect Inputs of various sorts and save their data to disk. And the next time you add the study, it will read the settings from the file on the path.
  • You will need to add two global assemblies: Newtonsoft.Json and the System.IO.
  • An additional class needs to be added to the namespace that can contain values used for the Serialization and file save process. I don't like having to use double the number of variables, it can lead to coding frustration when you forget to add a new variable to that section ... but it works. You just have to be careful
  • There are some functions at the bottom of the signal class that need to be copied, some code in the main constructor, and in the StartCalc() function.
I will probably look to improve it as I get more comfortable with MultiCharts. Try out your own improvements and please share any you find.
_TEST_save_file.Strategy.CS.txt
(5.9 KiB) Downloaded 11 times

Code: Select all

using System; using System.IO; // Add this global assembly using System.Drawing; using System.Linq; using PowerLanguage.Function; using ATCenterProxy.interop; using PowerLanguage.TradeManager; using Newtonsoft.Json; // Add this global assembly namespace PowerLanguage.Strategy { /// Inputs fields must match the [Input] fields you wish to use /// These are the fields saved to the directory public class Inputs { public int my_enum { get; set; } public double my_double { get; set; } public string my_string { get; set; } public int my_int32 { get; set; } public string my_trade_type { get; set; } } // end of Inputs class public class _TEST_save_file : SignalObject { Inputs inputs = new Inputs(); public enum parts_enum { Part1, Part2, }; [Input] public parts_enum my_enum { get; set; } [Input] public double my_double { get; set; } [Input] public string my_string { get; set; } [Input] public int my_int32 { get; set; } internal const string s1 = "long"; internal const string s2 = "short"; [Input(s1, s2)] public string my_trade_type { get; set; } // Set a variable to the Documents path. string docPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments); string prefsPath = "prefs_path"; string prefsFile = "settings_file.txt"; string fullPath = ""; public _TEST_save_file(object _ctx) : base(_ctx) { Output.Clear(); //prefsPath = this.GetType().Name+"_InputSettings"; //string tf = Bars.Info.Resolution.ToString(); //string symbol = Bars.Info.Name; //prefsFile = symbol + "_" + tf + "_" + this.GetType().Name; docPath = Path.Combine(docPath, prefsPath); System.IO.Directory.CreateDirectory(docPath); fullPath = Path.Combine(docPath, prefsFile); if (File.Exists(fullPath)) // try to read from preferences file { Output.WriteLine("Reading prefs from {0}", fullPath); Inputs saved_inputs = ReadFromJsonFile<Inputs>(fullPath); // this output is just for troubleshooting Output.WriteLine(">>> File Read, inputs are: " + "saved_inputs.my_enum={0}, saved_inputs.my_double={1}, " + "saved_inputs.my_string={2}, saved_inputs.my_int32={3}, saved_inputs.my_trade_type={4}", saved_inputs.my_enum, saved_inputs.my_double, saved_inputs.my_string, saved_inputs.my_int32, saved_inputs.my_trade_type); my_enum = (parts_enum)saved_inputs.my_enum; my_double = saved_inputs.my_double; my_string = saved_inputs.my_string; my_int32 = saved_inputs.my_int32; my_trade_type = saved_inputs.my_trade_type; } else // set defaults { my_enum = parts_enum.Part1; my_double = 5.010; my_string = "Account info"; my_int32 = 7; my_trade_type = s1; } } protected override void Create() { } protected override void StartCalc() { // load initial variables inputs.my_enum = (int)my_enum; inputs.my_double = my_double; inputs.my_string = my_string; inputs.my_int32 = my_int32; inputs.my_trade_type = my_trade_type; // this output is just for troubleshooting Output.WriteLine("inputs.my_enum={0}, inputs.my_double={1}, inputs.my_string={2}, inputs.my_int32={3}, inputs.my_trade_type={4}", inputs.my_enum, inputs.my_double, inputs.my_string, inputs.my_int32, inputs.my_trade_type); // after collecting changes, save to file Output.WriteLine("Saving prefs to {0}", fullPath); WriteToJsonFile<Inputs>(fullPath, inputs); } protected override void CalcBar(){ } /// <summary> /// Originally implemented as a class, changed it to class functions. /// See the blog (thanks Dan!) /// https://blog.danskingdom.com/saving-and-loading-a-c-objects-data-to-an-xml-json-or-binary-file/ /// <para>Requires the Newtonsoft.Json assembly (Json.Net package in NuGet Gallery) to be referenced in your project.</para> /// <typeparam name="T">The type of object being written to the file.</typeparam> /// <param name="filePath">The file path to write the object instance to.</param> /// <param name="objectToWrite">The object instance to write to the file.</param> /// <param name="append">If false the file will be overwritten if it already exists. If true the contents will be appended to the file.</param> /// public void WriteToJsonFile<T>(string filePath, T objectToWrite, bool append = false) //where T : new() { TextWriter writer = null; try { var contentsToWriteToFile = Newtonsoft.Json.JsonConvert.SerializeObject(objectToWrite); writer = new StreamWriter(filePath, append); writer.Write(contentsToWriteToFile); } finally { if (writer != null) writer.Close(); } } /// <summary> /// Reads an object instance from an Json file. /// <para>Object type must have a parameterless constructor.</para> /// </summary> /// <typeparam name="T">The type of object to read from the file.</typeparam> /// <param name="filePath">The file path to read the object instance from.</param> /// <returns>Returns a new instance of the object read from the Json file.</returns> public static T ReadFromJsonFile<T>(string filePath) where T : new() { TextReader reader = null; try { reader = new StreamReader(filePath); var fileContents = reader.ReadToEnd(); //Output.WriteLine(fileContents); return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(fileContents); } finally { if (reader != null) reader.Close(); } } } // end of study class } // end of namespace


Return to “MultiCharts .NET”