public static void LoadExternalFeatureBinary(Asset asset, ExternalFeatureData externalFeature, MessageDelegate messageDelegate)
        {
            //Read the results into the bars from the binary file that python has written over
            PreCalculatedFeatures pcFeatures = new PreCalculatedFeatures();

            //[ASSET] is used as a placeholder so insert the assetname here
            string filename = externalFeature.BinaryFilepath.Replace("[ASSET]", asset.Name);

            byte[] bytes = File.ReadAllBytes(filename);

            //Traverse the byte array to add in the values to the Data attribute of the corresponding bar
            int i = 0;

            while (i < bytes.Length)
            {
                //Read the required data from the byte array and increment the index
                long timestamp = BitConverter.ToInt64(bytes, i);
                i += 8;
                //convert from python to .net date
                DateTime dt = DateTime.FromBinary(timestamp / 100).AddYears(1969);

                //Add a new bar
                Dictionary <string, double?> barData = new Dictionary <string, double?>();
                pcFeatures.Data.Add(dt, barData);

                foreach (string field in externalFeature.FieldNames)
                {
                    double val = BitConverter.ToDouble(bytes, i);
                    barData.Add(field, val);
                    i += 8;
                }
            }

            //add this data to the asset (or overwrite if exists)
            if (!asset.Data.ContainsKey(externalFeature.Timeframe))
            {
                asset.Data.Add(externalFeature.Timeframe, pcFeatures);
            }
            else
            {
                asset.Data[externalFeature.Timeframe] = pcFeatures;
            }
        }
        public static void PythonFeatureBuilder(PythonBridge pb, Asset asset, ExternalFeatureData externalFeatureData, MessageDelegate messageDelegate = null)
        {
            messageDelegate?.Invoke(asset.Name + " Precalcualting Features ...");

            //Record start time of process for time taken message
            DateTime now = DateTime.Now;

            //build the features into a semicolon sepearated string
            string featureCommand = externalFeatureData.FeatureCommands;

            //get the dataset for this timeframe
            Dictionary <int, Bar[]> timeframes = DataBuilder.BuildTimeFrames(asset, new int[] { externalFeatureData.Timeframe });

            //Use a temporary Share directory for this data
            string filename = asset.DataPath.Replace(".bin", "_Share.bin");

            //Generate a tempory binary file containing the datafeed to be used for calculation and save to disk
            string datasetType = "single";

            if (externalFeatureData.CalculateOn == DataFeedType.Ask || externalFeatureData.CalculateOn == DataFeedType.Bid) //otherwise include all bid or ask OHLC and volume.
            {
                datasetType = "whole";
                DataBuilder.DatasetToBinary(filename, timeframes[externalFeatureData.Timeframe], externalFeatureData.CalculateOn);
            }
            else  //Faster way if just need a single data feed
            {
                DataBuilder.DatasetToBinarySingle(filename, timeframes[externalFeatureData.Timeframe], externalFeatureData.CalculateOn);
            }

            //[ASSET] is a placeholder
            string transformedFilename = externalFeatureData.BinaryFilepath.Replace("[ASSET]", asset.Name);

            //Bridge python to calculate the data
            string[] commands = new string[] { datasetType, filename, transformedFilename,
                                               externalFeatureData.FeatureCommands };
            pb.RunScript(FeatureBuildPath, commands);

            File.Delete(filename);

            messageDelegate?.Invoke(asset.Name + " feature calculations took: " + (DateTime.Now - now).TotalSeconds + " secs");
        }