Table of Contents

Создание утилит

Программа Hydra построена с возможностью создания собственных расширений. Для добавления собственного расширения необходимо скопировать dll в подпапку Plugins где установлена Hydra. Ниже представлен процесс создания утилиты резервирования имеющихся данных (она входит стандартно и представлена в качестве обучающего материала).

Каждая утилита должна реализовать интерфейс IHydraTask (или наследоваться от классов BaseHydraTask или ConnectorHydraTask`1):

	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>();

Важным этапом создания утилиты является реализация метода BaseHydraTask.OnProcess, в котором реализуется логика утилиты. В случае реализации резервирования, вся логика находится внутри 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();
			}
		}
	}