Table of Contents

Create new task

Hydra is built with the ability to create custom extensions. To add your own extension, you need to copy the dll to the Plugins subfolder where Hydra. is installed. Below is the process of creating a utility for backing up existing data (it is included as standard and is presented as training material).

Each utility has to implement the IHydraTask interface (or inherit from the BaseHydraTask or ConnectorHydraTask`1 classes):

	using System;
	using System.Collections.Generic;
	using System.ComponentModel.DataAnnotations;
	using System.IO;
	using System.Linq;
	using System.Security;
	using Amazon;
	using Ecng.Backup;
	using Ecng.Backup.Amazon;
	using Ecng.Backup.Yandex;
	using Ecng.Common;
	using Ecng.ComponentModel;
	using Ecng.Serialization;
	using StockSharp.Algo;
	using StockSharp.Algo.Storages;
	using StockSharp.Hydra.Core;
	using StockSharp.Localization;
	using StockSharp.Logging;
	using StockSharp.Messages;
	using DataType = StockSharp.Algo.DataType;
	enum BackupServices
	{
		AwsS3,
		AwsGlacier,
		Yandex,
	}
	[DisplayNameLoc(LocalizedStrings.BackupKey)]
	[DescriptionLoc(LocalizedStrings.BackupDescriptionKey)]
	[Doc("5a056352-64c7-41ea-87a8-2e112935e3b9.htm")]
	[Icon("backup_logo.png")]
	[MessageAdapterCategory(MessageAdapterCategories.Tool)]
	class BackupTask : BaseHydraTask
	{
		private const string _sourceName = LocalizedStrings.BackupKey;
		public BackupTask()
		{
			Offset = 1;
			StartFrom = new DateTime(2000, 1, 1);
			Interval = TimeSpan.FromDays(1);
			Service = BackupServices.AwsS3;
			Address = RegionEndpoint.USEast1.SystemName;
			ServiceRepo = "stocksharp";
			Login = string.Empty;
			Password = new SecureString();
		}
		[Display(
			ResourceType = typeof(LocalizedStrings),
			Name = LocalizedStrings.Str3427Key,
			Description = LocalizedStrings.Str3427Key + LocalizedStrings.Dot,
			GroupName = _sourceName,
			Order = 0)]
		public BackupServices Service { get; set; }
		[Display(
			ResourceType = typeof(LocalizedStrings),
			Name = LocalizedStrings.AddressKey,
			Description = LocalizedStrings.ServerAddressKey + LocalizedStrings.Dot,
			GroupName = _sourceName,
			Order = 1)]
		public string Address { get; set; }
		[Display(
			ResourceType = typeof(LocalizedStrings),
			Name = LocalizedStrings.Str1405Key,
			Description = LocalizedStrings.Str1405Key + LocalizedStrings.Dot,
			GroupName = _sourceName,
			Order = 2)]
		public string ServiceRepo { get; set; }
		[Display(
			ResourceType = typeof(LocalizedStrings),
			Name = LocalizedStrings.LoginKey,
			Description = LocalizedStrings.LoginKey + LocalizedStrings.Dot,
			GroupName = _sourceName,
			Order = 3)]
		public string Login { get; set; }
		[Display(
			ResourceType = typeof(LocalizedStrings),
			Name = LocalizedStrings.PasswordKey,
			Description = LocalizedStrings.PasswordKey + LocalizedStrings.Dot,
			GroupName = _sourceName,
			Order = 4)]
		public SecureString Password { get; set; }
		[Display(
			ResourceType = typeof(LocalizedStrings),
			Name = LocalizedStrings.Str2282Key,
			Description = LocalizedStrings.Str3779Key,
			GroupName = _sourceName,
			Order = 5)]
		public DateTime StartFrom { get; set; }
		[Display(
			ResourceType = typeof(LocalizedStrings),
			Name = LocalizedStrings.Str2284Key,
			Description = LocalizedStrings.Str3778Key,
			GroupName = _sourceName,
			Order = 6)]
		public int Offset { get; set; }
		public override void Save(SettingsStorage storage)
		{
			base.Save(storage);
			storage.SetValue(nameof(Service), Service);
			storage.SetValue(nameof(Address), Address);
			storage.SetValue(nameof(ServiceRepo), ServiceRepo);
			storage.SetValue(nameof(Login), Login);
			storage.SetValue(nameof(Password), Password);
			storage.SetValue(nameof(StartFrom), StartFrom);
			storage.SetValue(nameof(Offset), Offset);
		}
		public override void Load(SettingsStorage storage)
		{
			base.Load(storage);
			Service = storage.GetValue(nameof(Service), Service);
			Address = storage.GetValue(nameof(Address), Address);
			ServiceRepo = storage.GetValue(nameof(ServiceRepo), ServiceRepo);
			Login = storage.GetValue(nameof(Login), Login);
			Password = storage.GetValue(nameof(Password), Password);
			StartFrom = storage.GetValue(nameof(StartFrom), StartFrom);
			Offset = storage.GetValue(nameof(Offset), Offset);
		}
		public override IEnumerable<DataType> SupportedDataTypes => Enumerable.Empty<DataType>();

An important step in creating the utility is the implementation of the BaseHydraTask.OnProcess, method, which implements the utility logic. In the case of implementing data backup, all the logic is inside API:

	[	
		protected override TimeSpan OnProcess()
		{
			using (var service = CreateService())
			{
				var hasSecurities = false;
				this.AddInfoLog(LocalizedStrings.Str2306Params.Put(StartFrom));
				var startDate = StartFrom;
				var endDate = DateTime.Today - TimeSpan.FromDays(Offset);
				var allDates = startDate.Range(endDate, TimeSpan.FromDays(1)).ToArray();
				var pathEntry = ToEntry(new DirectoryInfo(Drive.Path));
				var workingSecurities = GetWorkingSecurities().ToArray();
				foreach (var date in allDates)
				{
					foreach (var security in workingSecurities)
					{
						hasSecurities = true;
						if (!CanProcess())
							break;
						var dateEntry = new BackupEntry
						{
							Name = LocalMarketDataDrive.GetDirName(date),
							Parent = new BackupEntry
							{
								Parent = new BackupEntry
								{
									Name = security.Security.Id.Substring(0, 1),
									Parent = pathEntry
								},
								Name = security.Security.Id,
							}
						};
						var dataTypes = Drive.GetAvailableDataTypes(security.Security.ToSecurityId(), StorageFormat);
						foreach (var dataType in dataTypes)
						{
							var storage = StorageRegistry.GetStorage(security.Security, dataType.MessageType, dataType.Arg, Drive, StorageFormat);
							var drive = storage.Drive;
							var stream = drive.LoadStream(date);
							if (stream == Stream.Null)
								continue;
							var entry = new BackupEntry
							{
								Name = LocalMarketDataDrive.GetFileName(dataType.MessageType, dataType.Arg, StorageFormat), // + LocalMarketDataDrive.GetExtension(StorageFormats.Binary),
								Parent = dateEntry,
							};
							var token = service.Upload(entry, stream, p => { });
							if (token.IsCancellationRequested)
							{
								this.AddWarningLog(LocalizedStrings.Cancelling);
								return TimeSpan.MaxValue;
							}
							this.AddInfoLog(LocalizedStrings.Str1580Params, GetPath(entry));
						}
					}
					if (CanProcess())
					{
						StartFrom += TimeSpan.FromDays(1);
						this.SaveSettings();
					}
				}
				if (!hasSecurities)
				{
					this.AddWarningLog(LocalizedStrings.Str2292);
					return TimeSpan.MaxValue;
				}
				if (CanProcess())
					this.AddInfoLog(LocalizedStrings.Str2300);
				return base.OnProcess();
			}
		}
		private static string GetPath(BackupEntry entry)
		{
			if (entry == null)
				return null;
			return GetPath(entry.Parent) + "/" + entry.Name;
		}
		private static BackupEntry ToEntry(DirectoryInfo di)
		{
			// is a disk
			if (di.Parent == null)
				return null;
			return new BackupEntry
			{
				Name = di.Name,
				Parent = di.Parent != null ? ToEntry(di.Parent) : null
			};
		}
		public override bool CanTestConnect => true;
		public override void TestConnect(Action<Exception> connectionChanged)
		{
			if (connectionChanged == null)
				throw new ArgumentNullException(nameof(connectionChanged));
			try
			{
				using (var service = CreateService())
					service.Find(null, string.Empty);
				connectionChanged(null);
			}
			catch (Exception ex)
			{
				connectionChanged(ex);
			}
		}
		private IBackupService CreateService()
		{
			switch (Service)
			{
				case BackupServices.AwsS3:
					return new AmazonS3Service(Address, ServiceRepo, Login, Password.To<string>());
				case BackupServices.AwsGlacier:
					return new AmazonGlacierService(Address, ServiceRepo, Login, Password.To<string>());
				case BackupServices.Yandex:
					return new YandexDiskService();
				default:
					throw new ArgumentOutOfRangeException();
			}
		}
	}