From 27a71bdf518e8d5252c7be1ff05e7e0589c97e11 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Oct 2025 14:55:18 +0000 Subject: [PATCH 1/3] Initial plan From e2051bc145c09a360979963482815979f49c5718 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Oct 2025 15:04:55 +0000 Subject: [PATCH 2/3] Add Avalonia UI project with minimal working implementation Co-authored-by: Capnode <37275126+Capnode@users.noreply.github.com> --- AVALONIA_MIGRATION.md | 200 ++++++++++++++++++ Algoloop.Avalonia/Algoloop.Avalonia.csproj | 24 +++ Algoloop.Avalonia/App.axaml | 9 + Algoloop.Avalonia/App.axaml.cs | 27 +++ Algoloop.Avalonia/MainWindow.axaml | 59 ++++++ Algoloop.Avalonia/MainWindow.axaml.cs | 11 + Algoloop.Avalonia/Program.cs | 20 ++ Algoloop.Avalonia/Resources/AlgoloopIcon.ico | Bin 0 -> 121576 bytes Algoloop.Avalonia/Styles.axaml | 18 ++ Algoloop.Avalonia/ViewModels/MainViewModel.cs | 68 ++++++ Algoloop.sln | 22 ++ 11 files changed, 458 insertions(+) create mode 100644 AVALONIA_MIGRATION.md create mode 100644 Algoloop.Avalonia/Algoloop.Avalonia.csproj create mode 100644 Algoloop.Avalonia/App.axaml create mode 100644 Algoloop.Avalonia/App.axaml.cs create mode 100644 Algoloop.Avalonia/MainWindow.axaml create mode 100644 Algoloop.Avalonia/MainWindow.axaml.cs create mode 100644 Algoloop.Avalonia/Program.cs create mode 100644 Algoloop.Avalonia/Resources/AlgoloopIcon.ico create mode 100644 Algoloop.Avalonia/Styles.axaml create mode 100644 Algoloop.Avalonia/ViewModels/MainViewModel.cs diff --git a/AVALONIA_MIGRATION.md b/AVALONIA_MIGRATION.md new file mode 100644 index 000000000..49304ba68 --- /dev/null +++ b/AVALONIA_MIGRATION.md @@ -0,0 +1,200 @@ +# Avalonia UI Migration Documentation + +## Overview + +This document describes the migration from WPF to Avalonia UI for the Algoloop application. The Avalonia UI project provides a cross-platform alternative to the existing WPF application. + +## What Changed + +### New Project: Algoloop.Avalonia + +A new Avalonia UI project has been added to the solution targeting .NET 8.0. This project: +- Uses Avalonia 11.2.2 for cross-platform UI +- Implements a minimal main window with menu and tabbed interface +- Provides standalone ViewModels without WPF dependencies +- Includes the FluentTheme for modern UI styling + +### Project Structure + +``` +Algoloop.Avalonia/ +├── Algoloop.Avalonia.csproj # Project file +├── Program.cs # Application entry point +├── App.axaml # Application definition +├── App.axaml.cs # Application code-behind +├── MainWindow.axaml # Main window XAML +├── MainWindow.axaml.cs # Main window code-behind +├── Styles.axaml # Application styles +├── ViewModels/ +│ └── MainViewModel.cs # Main view model +└── Resources/ + └── AlgoloopIcon.ico # Application icon +``` + +### Key Components + +#### Program.cs +The entry point that configures and starts the Avalonia application with classic desktop lifetime. + +#### App.axaml / App.axaml.cs +The main application class that: +- Loads the FluentTheme +- Initializes the application styles +- Creates and shows the main window + +#### MainWindow.axaml / MainWindow.axaml.cs +The main application window featuring: +- File menu with Save and Exit commands +- Help menu +- Status bar +- Tabbed interface with: + - Markets (placeholder) + - Strategies (placeholder) + - Research (placeholder) + - Log (DataGrid with sample data) + +#### ViewModels +Simplified ViewModels created specifically for Avalonia: +- **MainViewModel**: Main application view model with Save and Exit commands +- **LogViewModel**: Log view model with sample log entries + +### Dependencies + +The Avalonia project uses the following NuGet packages: +- Avalonia 11.2.2 +- Avalonia.Desktop 11.2.2 +- Avalonia.Controls.DataGrid 11.2.2 +- Avalonia.Themes.Fluent 11.2.2 +- Avalonia.Diagnostics 11.2.2 (Debug only) +- CommunityToolkit.Mvvm 8.4.0 + +## How to Build and Run + +### Prerequisites +- .NET 8.0 SDK or later +- Platform-specific requirements: + - Windows: No additional requirements + - Linux: See [Avalonia Linux setup](https://docs.avaloniaui.net/docs/get-started/set-up-an-editor/linux) + - macOS: See [Avalonia macOS setup](https://docs.avaloniaui.net/docs/get-started/set-up-an-editor/macos) + +### Build Instructions + +#### Build the Avalonia project: +```bash +dotnet build Algoloop.Avalonia/Algoloop.Avalonia.csproj +``` + +#### Build the entire solution: +```bash +dotnet build Algoloop.sln +``` + +### Run Instructions + +#### Run the Avalonia UI application: +```bash +dotnet run --project Algoloop.Avalonia +``` + +#### On Windows, you can also run the executable directly: +```bash +.\Algoloop.Avalonia\bin\Debug\net8.0\Algoloop.Avalonia.exe +``` + +## Known Limitations + +### Current Implementation Status + +1. **Minimal UI Implementation** + - The current implementation provides a basic window structure + - Only the Log tab has a functional DataGrid with sample data + - Markets, Strategies, and Research tabs show placeholders + +2. **ViewModels** + - Simplified ViewModels created specifically for Avalonia + - No integration with existing WPF ViewModels (which have WPF dependencies) + - Business logic from Algoloop core projects needs to be integrated + +3. **Missing Features** + - No integration with QuantConnect Lean engine + - No data loading or persistence + - No charts or visualizations + - No strategy/market management + - No settings or configuration UI + +4. **Platform-Specific Limitations** + - The original WPF ViewModels use System.Windows which is Windows-specific + - Some features may require platform-specific implementations + +### WPF Project Status + +- The WPF project (Algoloop.Wpf) remains unchanged and fully functional +- Both WPF and Avalonia projects can coexist in the solution +- WPF is still the primary/recommended UI for production use + +## Next Steps for Full Migration + +To achieve feature parity with the WPF version, the following work is needed: + +1. **Refactor ViewModels** + - Extract business logic from WPF-specific ViewModels + - Create platform-agnostic ViewModels or shared view model library + - Implement proper MVVM patterns without WPF dependencies + +2. **Implement Core Features** + - Markets management UI + - Strategies management UI + - Research/Jupyter notebook integration + - Settings and configuration + - Chart visualizations (using OxyPlot.Avalonia or similar) + +3. **Integrate Business Logic** + - Connect to QuantConnect Lean engine + - Implement data loading and persistence + - Add backtesting capabilities + - Integrate market data providers + +4. **Testing and Validation** + - Cross-platform testing (Windows, Linux, macOS) + - UI/UX refinement + - Performance optimization + +5. **Documentation** + - User guide for Avalonia version + - Developer documentation + - Migration guide for contributors + +## Architecture Notes + +### Separation of Concerns + +The Avalonia implementation intentionally avoids direct references to WPF projects to maintain cross-platform compatibility. Future development should: +- Keep UI code platform-agnostic where possible +- Use dependency injection for platform-specific services +- Share business logic through non-UI projects + +### Recommended Approach + +For a production-ready Avalonia migration: +1. Create shared ViewModel library (no WPF/Avalonia dependencies) +2. Use interfaces for platform-specific services +3. Implement proper dependency injection +4. Use platform-specific view implementations (WPF/Avalonia) + +## References + +- [Avalonia Documentation](https://docs.avaloniaui.net/) +- [Avalonia Samples](https://github.com/AvaloniaUI/Avalonia.Samples) +- [MVVM Toolkit](https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/) +- [WPF to Avalonia Migration Guide](https://docs.avaloniaui.net/docs/guides/platforms/wpf-migration) + +## Support + +For questions or issues related to the Avalonia migration: +1. Check existing documentation +2. Review Avalonia samples and documentation +3. Create an issue in the repository with "Avalonia" label + +## License + +The Avalonia project follows the same license as the main Algoloop project. diff --git a/Algoloop.Avalonia/Algoloop.Avalonia.csproj b/Algoloop.Avalonia/Algoloop.Avalonia.csproj new file mode 100644 index 000000000..902263960 --- /dev/null +++ b/Algoloop.Avalonia/Algoloop.Avalonia.csproj @@ -0,0 +1,24 @@ + + + WinExe + net8.0 + enable + true + Resources\AlgoloopIcon.ico + false + + + + + + + + + + + + + + + + diff --git a/Algoloop.Avalonia/App.axaml b/Algoloop.Avalonia/App.axaml new file mode 100644 index 000000000..90538a0c9 --- /dev/null +++ b/Algoloop.Avalonia/App.axaml @@ -0,0 +1,9 @@ + + + + + + diff --git a/Algoloop.Avalonia/App.axaml.cs b/Algoloop.Avalonia/App.axaml.cs new file mode 100644 index 000000000..3b1cd83b1 --- /dev/null +++ b/Algoloop.Avalonia/App.axaml.cs @@ -0,0 +1,27 @@ +using Algoloop.Avalonia.ViewModels; +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; + +namespace Algoloop.Avalonia; + +public partial class App : Application +{ + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow = new MainWindow + { + DataContext = new MainViewModel() + }; + } + + base.OnFrameworkInitializationCompleted(); + } +} diff --git a/Algoloop.Avalonia/MainWindow.axaml b/Algoloop.Avalonia/MainWindow.axaml new file mode 100644 index 000000000..4dd7127af --- /dev/null +++ b/Algoloop.Avalonia/MainWindow.axaml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Algoloop.Avalonia/MainWindow.axaml.cs b/Algoloop.Avalonia/MainWindow.axaml.cs new file mode 100644 index 000000000..94d99a7a3 --- /dev/null +++ b/Algoloop.Avalonia/MainWindow.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace Algoloop.Avalonia; + +public partial class MainWindow : Window +{ + public MainWindow() + { + InitializeComponent(); + } +} diff --git a/Algoloop.Avalonia/Program.cs b/Algoloop.Avalonia/Program.cs new file mode 100644 index 000000000..2c74e152f --- /dev/null +++ b/Algoloop.Avalonia/Program.cs @@ -0,0 +1,20 @@ +using Avalonia; +using System; + +namespace Algoloop.Avalonia; + +class Program +{ + // Initialization code. Don't use any Avalonia, third-party APIs or any + // SynchronizationContext-reliant code before AppMain is called: things aren't initialized + // yet and stuff might break. + [STAThread] + public static void Main(string[] args) => BuildAvaloniaApp() + .StartWithClassicDesktopLifetime(args); + + // Avalonia configuration, don't remove; also used by visual designer. + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .UsePlatformDetect() + .LogToTrace(); +} diff --git a/Algoloop.Avalonia/Resources/AlgoloopIcon.ico b/Algoloop.Avalonia/Resources/AlgoloopIcon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c1ca3ca5465af9b79ffa4ab3ba6bdbe5ae202712 GIT binary patch literal 121576 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 TS` + + + + + + + + diff --git a/Algoloop.Avalonia/ViewModels/MainViewModel.cs b/Algoloop.Avalonia/ViewModels/MainViewModel.cs new file mode 100644 index 000000000..a6b5ce27f --- /dev/null +++ b/Algoloop.Avalonia/ViewModels/MainViewModel.cs @@ -0,0 +1,68 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using System.Collections.ObjectModel; + +namespace Algoloop.Avalonia.ViewModels; + +public class LogItem +{ + public string Time { get; set; } = string.Empty; + public string Level { get; set; } = string.Empty; + public string Message { get; set; } = string.Empty; +} + +public class LogViewModel : ObservableObject +{ + public ObservableCollection Logs { get; } = new(); + + public LogViewModel() + { + // Add some sample log items + Logs.Add(new LogItem { Time = System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), Level = "INFO", Message = "Application started" }); + } +} + +public class MainViewModel : ObservableObject +{ + private bool _isBusy; + private string _statusMessage = "Ready"; + + public MainViewModel() + { + SaveCommand = new RelayCommand(SaveConfig, () => !IsBusy); + ExitCommand = new RelayCommand(DoExit); + LogViewModel = new LogViewModel(); + } + + public static string Title => "Algoloop Avalonia (Preview)"; + + public bool IsBusy + { + get => _isBusy; + set => SetProperty(ref _isBusy, value); + } + + public string StatusMessage + { + get => _statusMessage; + set => SetProperty(ref _statusMessage, value); + } + + public LogViewModel LogViewModel { get; } + + public RelayCommand SaveCommand { get; } + public RelayCommand ExitCommand { get; } + + private void SaveConfig() + { + StatusMessage = "Configuration saved"; + } + + private void DoExit(object? window) + { + if (window is global::Avalonia.Controls.Window w) + { + w.Close(); + } + } +} diff --git a/Algoloop.sln b/Algoloop.sln index 7335e4160..ac1fd9bed 100644 --- a/Algoloop.sln +++ b/Algoloop.sln @@ -63,6 +63,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Algoloop", "Algoloop\Algolo EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuantConnect.DownloaderDataProvider.Launcher", "DownloaderDataProvider\QuantConnect.DownloaderDataProvider.Launcher.csproj", "{387365D5-6937-4211-AA79-CBE46CCB3961}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Algoloop.Avalonia", "Algoloop.Avalonia\Algoloop.Avalonia.csproj", "{8E55E25D-F304-469F-B676-431E6779ECE1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -667,6 +669,26 @@ Global {387365D5-6937-4211-AA79-CBE46CCB3961}.Release|x64.Build.0 = Release|Any CPU {387365D5-6937-4211-AA79-CBE46CCB3961}.Release|x86.ActiveCfg = Release|Any CPU {387365D5-6937-4211-AA79-CBE46CCB3961}.Release|x86.Build.0 = Release|Any CPU + {8E55E25D-F304-469F-B676-431E6779ECE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E55E25D-F304-469F-B676-431E6779ECE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E55E25D-F304-469F-B676-431E6779ECE1}.Debug|ARM.ActiveCfg = Debug|Any CPU + {8E55E25D-F304-469F-B676-431E6779ECE1}.Debug|ARM.Build.0 = Debug|Any CPU + {8E55E25D-F304-469F-B676-431E6779ECE1}.Debug|arm64.ActiveCfg = Debug|Any CPU + {8E55E25D-F304-469F-B676-431E6779ECE1}.Debug|arm64.Build.0 = Debug|Any CPU + {8E55E25D-F304-469F-B676-431E6779ECE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {8E55E25D-F304-469F-B676-431E6779ECE1}.Debug|x64.Build.0 = Debug|Any CPU + {8E55E25D-F304-469F-B676-431E6779ECE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {8E55E25D-F304-469F-B676-431E6779ECE1}.Debug|x86.Build.0 = Debug|Any CPU + {8E55E25D-F304-469F-B676-431E6779ECE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E55E25D-F304-469F-B676-431E6779ECE1}.Release|Any CPU.Build.0 = Release|Any CPU + {8E55E25D-F304-469F-B676-431E6779ECE1}.Release|ARM.ActiveCfg = Release|Any CPU + {8E55E25D-F304-469F-B676-431E6779ECE1}.Release|ARM.Build.0 = Release|Any CPU + {8E55E25D-F304-469F-B676-431E6779ECE1}.Release|arm64.ActiveCfg = Release|Any CPU + {8E55E25D-F304-469F-B676-431E6779ECE1}.Release|arm64.Build.0 = Release|Any CPU + {8E55E25D-F304-469F-B676-431E6779ECE1}.Release|x64.ActiveCfg = Release|Any CPU + {8E55E25D-F304-469F-B676-431E6779ECE1}.Release|x64.Build.0 = Release|Any CPU + {8E55E25D-F304-469F-B676-431E6779ECE1}.Release|x86.ActiveCfg = Release|Any CPU + {8E55E25D-F304-469F-B676-431E6779ECE1}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From ca83954e635de5ccf649349f0fe797d3dc5aa64b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Oct 2025 15:06:29 +0000 Subject: [PATCH 3/3] Add Avalonia project README Co-authored-by: Capnode <37275126+Capnode@users.noreply.github.com> --- Algoloop.Avalonia/README.md | 66 +++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 Algoloop.Avalonia/README.md diff --git a/Algoloop.Avalonia/README.md b/Algoloop.Avalonia/README.md new file mode 100644 index 000000000..e94143bba --- /dev/null +++ b/Algoloop.Avalonia/README.md @@ -0,0 +1,66 @@ +# Algoloop Avalonia UI + +This is the cross-platform Avalonia UI implementation for Algoloop. + +## Quick Start + +### Prerequisites +- .NET 8.0 SDK or later + +### Build +```bash +dotnet build +``` + +### Run +```bash +dotnet run +``` + +## Project Structure + +- **Program.cs** - Application entry point +- **App.axaml** / **App.axaml.cs** - Application configuration with FluentTheme +- **MainWindow.axaml** / **MainWindow.axaml.cs** - Main application window +- **ViewModels/** - MVVM view models +- **Styles.axaml** - Application-wide styles +- **Resources/** - Application resources (icons, etc.) + +## Features + +### Current Implementation +- ✅ Cross-platform desktop application (Windows, Linux, macOS) +- ✅ FluentTheme UI +- ✅ Main window with menu and tabs +- ✅ DataGrid in Log tab +- ✅ MVVM architecture using CommunityToolkit.Mvvm + +### Planned Features +- ⏳ Markets management +- ⏳ Strategies configuration +- ⏳ Research/Jupyter integration +- ⏳ Chart visualizations +- ⏳ QuantConnect Lean engine integration +- ⏳ Data persistence +- ⏳ Backtesting capabilities + +## Development + +### Adding New Views +1. Create `.axaml` and `.axaml.cs` files in the appropriate folder +2. Create corresponding ViewModel in `ViewModels/` +3. Wire up DataContext in code-behind or XAML + +### Styling +Application-wide styles are defined in `Styles.axaml`. The project uses the FluentTheme from Avalonia. + +### Debugging +The project includes Avalonia.Diagnostics for development builds. Press F12 in the running application to open the DevTools. + +## Documentation + +For comprehensive migration information, see [AVALONIA_MIGRATION.md](../AVALONIA_MIGRATION.md) in the repository root. + +## License + +Same license as the main Algoloop project.