В реальном приложении у нас будет много стратегий со своими параметрами, которые проще всего загрузить из XAML файла. XAML — это декларативный язык разметки. Он упрощает создание пользовательского интерфейса для нашего приложения. К настоящему моменту мы уже проделали большую работу по разделению бизнес-логики от UI и теперь можем в форме писать кода типа: StandardStrategy strategy; StandardStrategy CreateStrategy() { var strategy = new StrikeStrategy() { TraderBuilder = new QuikTraderBuilder() { Path = \"***\", Login = \"***\", Password = \"***\" }, PortfolioSelector = new PortfolioSelector() { PortfolioName = \"***\" }, SecuritySelector = new SecuritySelector() { SecurityCode = \"RIM2\" }, HistoryCandleProvider = new FinamHistoryCandleProvider() { TimeFrame = TimeSpan.FromHours(1), FinamSecurityCode = 80996 }, SettingsProvider = new RegistrySettingsProvider() { SubKey = @\"Software\\FinDirector\\StrikeStrategy\" }, VolumeSizer = new MarginVolumeSizer() { Ratio = 0.2, MaxCapital = 1000000 }, TimeFrame = TimeSpan.FromMinutes(5) }; return strategy; } private void Window_Loaded(object sender, RoutedEventArgs e) { strategy = CreateStrategy(); } protected override void OnClosed(EventArgs e) { base.OnClosed(e); if (strategy != null \u0026\u0026 strategy.TraderBuilder != null) strategy.TraderBuilder.Dispose(); } private void btnStart_Click(object sender, RoutedEventArgs e) { if (strategy.ProcessState == ProcessStates.Stopped) Task.Factory.StartNew(strategy.Start); } private void btnStop_Click(object sender, RoutedEventArgs e) { strategy.Stop(); } Получилось совсем немного кода. Тем не менее в реальном приложении у нас будет много стратегий и у каждой будет куча параметров. Чтобы не зашивать их в коде, проще всего загрузить их из XAML файла, таком как в примере из статьи “Создание роботов с помощью S#. Введение”. public class StrategyLoader : FrameworkElement { public StrategyList Strategies { get; set; } public StrategyLoader() { Strategies = new StrategyList(); } public static StrategyLoader Load(string location) { return (StrategyLoader)XamlReader.Load(new XmlTextReader(location)); } } public class StrategyList : List { } Теперь изменим метод CreateStrategy нашей формы: StandardStrategy CreateStrategy() { var strategy = StrategyLoader.Load(\"Strategies.config\").Strategies.FirstOrDefault(); return strategy; } А в файл Strategies.config поместим все настройки. В коде формы теперь нет ничего лишнего. Автор статьи: Вадим Чижов
Самое главное в роботе — это обработка исключений. У нас в роботе могут быть запущено множество различных стратегий, и исключение в одном из роботов или ошибка в UI потоке не должны приводить к падению всей программы. Тем не менее именно такое поведение характерно по умолчанию для .NET программы. Далее считаем, что наш робот является WPF – приложением! В качестве теста используем следующий код: private void Window_Loaded(object sender, RoutedEventArgs e) { Task.Factory.StartNew(ThrowException); ThrowException(); } void ThrowException() { throw new Exception(\"ops!\"); } Вначале мы кидаем исключение в отдельном потоке (а в стратегиях события могут вызываться асинхронно), а затем в UI потоке. Приложение упадет. Чтобы это исправить, необходимо сделать следующие правки. В файле App.xaml.cs написать: protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); #if (DEBUG != true) // Don\u0027t handle the exceptions in Debug mode because otherwise the Debugger wouldn\u0027t // jump into the code when an exception occurs. DispatcherUnhandledException += AppDispatcherUnhandledException; AppDomain.CurrentDomain.UnhandledException += AppDomainUnhandledException; #endif } private void AppDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) { HandleException(e.Exception, false); e.Handled = true; } private static void AppDomainUnhandledException(object sender, UnhandledExceptionEventArgs e) { HandleException(e.ExceptionObject as Exception, e.IsTerminating); } private static void HandleException(Exception e, bool isTerminating) { if (e == null) return; Trace.TraceError(e.ToString()); if (!isTerminating) { MessageBox.Show(string.Format(CultureInfo.CurrentCulture, \"Неизвестная ошибка: {0}\", e.ToString()), \"FinDirector\", MessageBoxButton.OK, MessageBoxImage.Error); } } Тем не менее, это не спасет от крэша наше приложение при необработанных исключениях не в UI потоке. В файл app.confg необходимо добавить строки: Теперь: приложение не будет падать при не критических исключениях (таких как OutOfMemory, StackOverflow); мы получим сообщение об ошибке, а не проглотим его. Автор статьи — Вадим Чижов