Initial Commit

This commit is contained in:
2025-06-10 19:03:43 -04:00
commit 93ab70180a
62 changed files with 49312 additions and 0 deletions

View File

@@ -0,0 +1,61 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
xmlns:vm="using:PortfolioManager.Dialogs"
x:Class="PortfolioManager.Dialogs.ClosePositionDialog"
x:DataType="vm:ClosePositionDialogViewModel"
Title="Close Position Dialog"
Width="640" Height="350"
CanResize="false"
WindowStartupLocation="CenterOwner">
<StackPanel Margin="5" Orientation="Vertical">
<Grid DockPanel.Dock="Left" Margin="0,2,4,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="6" />
<ColumnDefinition Width="75*" />
<ColumnDefinition Width="6" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="25*" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
</Grid.RowDefinitions>
<Label FontWeight="Bold" FontSize="16" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Content="{Binding Path=CompanyName, Mode=OneWay}"/>
<Label Grid.Row="2" Grid.Column="0">Symbol</Label>
<TextBox Margin="0,4,0,4" Grid.Row="2" Grid.Column="2" IsReadOnly="true" MinWidth="35" Text="{Binding Path=Symbol, Mode=OneWay}"/>
<Label Grid.Row="3" Grid.Column="0" MinWidth="35">Purchase Date</Label>
<TextBox Margin="0,4,0,4" IsReadOnly="true" Grid.Row="3" Grid.Column="2" Text="{Binding Path=PurchaseDate, Mode=OneWay}"/>
<Label Grid.Row="4" Grid.Column="0" MinWidth="35">Sell Date</Label>
<TextBox Margin="0,4,0,4" IsReadOnly="false" Grid.Row="4" Grid.Column="2" Text="{Binding Path=SellDate, UpdateSourceTrigger=LostFocus, Mode=TwoWay}"/>
<Label Grid.Row="5" Grid.Column="0" MinWidth="35">Sell Price</Label>
<TextBox Margin="0,4,0,4" Grid.Row="5" Grid.Column="2" MinWidth="75" Text="{Binding Path=SellPrice, UpdateSourceTrigger=LostFocus, Mode=TwoWay}"/>
<TextBox Margin="0,4,0,4" Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="5" Text="{Binding Path=Message, Mode=TwoWay}"/>
<Button Command="{Binding Path=OkButtonClickCommand}" Content="_Ok" MinWidth="70" IsDefault="False" Margin="2" Grid.Row="9" Grid.RowSpan="2" Grid.Column="0" IsEnabled="{Binding Path=OkEnabled, , Mode=TwoWay}"/>
<Button Command="{Binding Path=CancelButtonClick}" Content="_Cancel" MinWidth="70" IsCancel="True" Margin="2" Grid.Row="11" Grid.RowSpan="2" Grid.Column="0"/>
</Grid>
</StackPanel>
</Window>

View File

@@ -0,0 +1,13 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace PortfolioManager.Dialogs;
public partial class ClosePositionDialog : Window
{
public ClosePositionDialog()
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,185 @@
using System;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Rendering.Composition;
using CommunityToolkit.Mvvm.Input;
using MarketData;
using MarketData.DataAccess;
using MarketData.Generator.Interface;
using MarketData.MarketDataModel;
using MarketData.Utils;
using PortfolioManager.ViewModels;
namespace PortfolioManager.Dialogs
{
public partial class ClosePositionDialogViewModel : DialogViewModelBase
{
// private IPosition sourcePosition;
private IPurePosition sourcePosition;
private String message = String.Empty;
private bool okEnabled = false;
public ClosePositionDialogViewModel(Window dialogWindow, IPurePosition clonedPosition)
: base(dialogWindow)
{
sourcePosition = clonedPosition;
if (Utility.IsEpoch(sourcePosition.SellDate))
{
sourcePosition.SellDate = DateTime.Now;
}
OkEnabled = Validate();
DisplayName = "Close Position";
}
public String Symbol
{
get { return sourcePosition.Symbol; }
}
public String CompanyName
{
get
{
CompanyProfile companyProfile = CompanyProfileDA.GetCompanyProfile(sourcePosition.Symbol);
return companyProfile.CompanyName;
}
}
public String PurchaseDate
{
get
{
return sourcePosition.PurchaseDate.ToShortDateString();
}
}
public String SellDate
{
get
{
return sourcePosition.SellDate.ToShortDateString();
}
set
{
try
{
sourcePosition.SellDate = Utility.ParseDate(value);
base.OnPropertyChanged("SellDate");
OkEnabled = Validate();
}
catch (Exception)
{
Message = "SellDate must be a valid date.";
}
}
}
public String SellPrice
{
get
{
return Utility.FormatCurrency(sourcePosition.CurrentPrice, 3);
}
set
{
try
{
sourcePosition.CurrentPrice = Utility.ParseCurrency(value);
base.OnPropertyChanged("SellPrice");
OkEnabled = Validate();
}
catch (Exception)
{
Message = "Sell price must be a valid number.";
}
}
}
[RelayCommand(CanExecute =nameof(Validate))]
public async Task OkButtonClick()
{
IsSuccess = true;
await Close();
}
[RelayCommand]
public async Task CancelButtonClick()
{
await Close();
}
public IPurePosition GetResult()
{
return sourcePosition;
}
public String Message
{
get
{
return message;
}
set
{
message = value;
base.OnPropertyChanged("Message");
}
}
public void SetMessage(String message)
{
Message = message;
}
public bool OkEnabled
{
get
{
return okEnabled;
}
set
{
okEnabled = value;
base.OnPropertyChanged("OkEnabled");
}
}
private bool Validate()
{
DateGenerator dateGenerator = new DateGenerator();
if (null == Symbol)
{
SetMessage("Invalid Symbol.");
return false;
}
if (Utility.IsEpoch(sourcePosition.PurchaseDate))
{
SetMessage("Invalid Purchase Date.");
return false;
}
if (Utility.IsEpoch(sourcePosition.SellDate))
{
SetMessage("Invalid Sell Date.");
return false;
}
if (!dateGenerator.IsMarketOpen(sourcePosition.SellDate))
{
SetMessage("Market closed on Sell Date.");
return false;
}
if (double.IsNaN(sourcePosition.CurrentPrice))
{
SetMessage("Invalid Sell Price.");
return false;
}
SetMessage("");
return true;
}
}
}

View File

@@ -0,0 +1,64 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
xmlns:vm="using:PortfolioManager.Dialogs"
x:Class="PortfolioManager.Dialogs.EditPositionDialog"
x:DataType="vm:EditPositionDialogViewModel"
Title="Edit Position Dialog"
Width="640" Height="350"
CanResize="false"
WindowStartupLocation="CenterOwner">
<StackPanel Margin="5" Orientation="Vertical">
<Grid DockPanel.Dock="Left" Margin="0,2,4,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="6" />
<ColumnDefinition Width="75*" />
<ColumnDefinition Width="6" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="25*" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
</Grid.RowDefinitions>
<Label FontWeight="Bold" FontSize="16" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Content="{Binding Path=CompanyName, Mode=OneWay}"/>
<Label Grid.Row="2" Grid.Column="0">Symbol</Label>
<TextBox Margin="0,4,0,4" Grid.Row="2" Grid.Column="2" IsReadOnly="true" MinWidth="35" Text="{Binding Path=Symbol, Mode=OneWay}"/>
<Label Grid.Row="3" Grid.Column="0" MinWidth="35">Purchase Date</Label>
<TextBox Margin="0,4,0,4" IsReadOnly="true" Grid.Row="3" Grid.Column="2" Text="{Binding Path=PurchaseDate, Mode=OneWay}"/>
<Label Grid.Row="4" Grid.Column="0" MinWidth="75">Purchase Price</Label>
<TextBox Margin="0,4,0,4" Grid.Row="4" Grid.Column="2" IsReadOnly="false" Text="{Binding Path=PurchasePrice, UpdateSourceTrigger=LostFocus, Mode=TwoWay}" />
<Label Grid.Row="5" Grid.Column="0">Initial Stop Limit</Label>
<TextBox Margin="0,4,0,4" Grid.Row="5" Grid.Column="2" MinWidth="35" Text="{Binding Path=InitialStopLimit, UpdateSourceTrigger=LostFocus, Mode=TwoWay}"/>
<CheckBox Grid.Row="6" Grid.Column="0" IsChecked="{Binding Path=SyncTrailingStop, UpdateSourceTrigger=LostFocus, Mode=TwoWay}">Sync Trailing Stop</CheckBox>
<Label Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="3" FontWeight="Bold" Content="{Binding Path=InitialStopRecommendation, Mode=OneWay}" />
<Label Grid.Row="8" Grid.Column="0">Trailing Stop</Label>
<TextBox Grid.Row="8" Grid.Column="2" MinWidth="35" Text="{Binding Path=TrailingStopLimit, UpdateSourceTrigger=LostFocus, Mode=TwoWay}"/>
<Button Command="{Binding Path=OkButtonClickCommand}" Content="_Ok" MinWidth="70" IsDefault="False" Margin="2" Grid.Row="9" Grid.RowSpan="2" Grid.Column="0"/>
<Button Command="{Binding Path=CancelButtonClick}" Content="_Cancel" MinWidth="70" IsCancel="True" Margin="2" Grid.Row="11" Grid.RowSpan="2" Grid.Column="0"/>
</Grid>
</StackPanel>
</Window>

View File

@@ -0,0 +1,13 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace PortfolioManager.Dialogs;
public partial class EditPositionDialog : Window
{
public EditPositionDialog()
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,56 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
xmlns:vm="using:PortfolioManager.Dialogs"
x:Class="PortfolioManager.Dialogs.EditPositionDialogNoStop"
Title="EditPositionDialogNoStop"
x:DataType="vm:EditPositionDialogNoStopViewModel"
Width="640" Height="350"
CanResize="false"
WindowStartupLocation="CenterOwner">
<StackPanel Margin="5" Orientation="Vertical">
<Grid DockPanel.Dock="Left" Margin="0,2,4,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="6" />
<ColumnDefinition Width="75*" />
<ColumnDefinition Width="6" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="25*" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
</Grid.RowDefinitions>
<Label FontWeight="Bold" FontSize="16" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Content="{Binding Path=CompanyName, Mode=OneWay}"/>
<Label Grid.Row="2" Grid.Column="0">Symbol</Label>
<TextBox Margin="0,4,0,4" Grid.Row="2" Grid.Column="2" IsReadOnly="true" MinWidth="35" Text="{Binding Path=Symbol, Mode=OneWay}"/>
<Label Grid.Row="3" Grid.Column="0" MinWidth="35">Purchase Date</Label>
<TextBox Margin="0,4,0,4" IsReadOnly="true" Grid.Row="3" Grid.Column="2" Text="{Binding Path=PurchaseDate, Mode=OneWay}"/>
<Label Grid.Row="4" Grid.Column="0" MinWidth="75">Purchase Price</Label>
<TextBox Margin="0,4,0,4" Grid.Row="4" Grid.Column="2" IsReadOnly="false" Text="{Binding Path=PurchasePrice, UpdateSourceTrigger=LostFocus, Mode=TwoWay}" />
<Button Command="{Binding Path=OkButtonClickCommand}" Content="_Ok" MinWidth="70" IsDefault="False" Margin="2" Grid.Row="9" Grid.RowSpan="2" Grid.Column="0"/>
<Button Command="{Binding Path=CancelButtonClick}" Content="_Cancel" MinWidth="70" IsCancel="True" Margin="2" Grid.Row="11" Grid.RowSpan="2" Grid.Column="0"/>
</Grid>
</StackPanel>
</Window>

View File

@@ -0,0 +1,13 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace PortfolioManager.Dialogs;
public partial class EditPositionDialogNoStop : Window
{
public EditPositionDialogNoStop()
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,77 @@
using System;
using System.Threading.Tasks;
using Avalonia.Controls;
using CommunityToolkit.Mvvm.Input;
using MarketData.DataAccess;
using MarketData.Generator.Interface;
using MarketData.MarketDataModel;
using MarketData.Utils;
using PortfolioManager.ViewModels;
namespace PortfolioManager.Dialogs
{
public partial class EditPositionDialogNoStopViewModel : DialogViewModelBase
{
private IPurePosition sourcePosition;
public EditPositionDialogNoStopViewModel(Window dialogWindow, IPurePosition clonedPosition)
: base(dialogWindow)
{
sourcePosition = clonedPosition;
DisplayName = "Edit Position";
}
public String Symbol
{
get { return sourcePosition.Symbol; }
}
public String CompanyName
{
get
{
CompanyProfile companyProfile = CompanyProfileDA.GetCompanyProfile(sourcePosition.Symbol);
return companyProfile.CompanyName;
}
}
public String PurchaseDate
{
get
{
return sourcePosition.PurchaseDate.ToShortDateString();
}
}
public String PurchasePrice
{
get
{
return Utility.FormatCurrency(sourcePosition.PurchasePrice);
}
set
{
sourcePosition.PurchasePrice = Utility.ParseCurrency(value);
base.OnPropertyChanged("PurchasePrice");
}
}
public IPurePosition GetResult()
{
return sourcePosition;
}
[RelayCommand]
public async Task OkButtonClick()
{
IsSuccess = true;
await Close();
}
[RelayCommand]
public async Task CancelButtonClick()
{
await Close();
}
}
}

View File

@@ -0,0 +1,131 @@
using System;
using System.Threading.Tasks;
using Avalonia.Controls;
using CommunityToolkit.Mvvm.Input;
using MarketData.DataAccess;
using MarketData.Generator.Interface;
using MarketData.MarketDataModel;
using MarketData.Utils;
using PortfolioManager.ViewModels;
namespace PortfolioManager.Dialogs
{
public partial class EditPositionDialogViewModel : DialogViewModelBase
{
private IPosition sourcePosition;
private bool syncTrailingStop = true;
public EditPositionDialogViewModel(Window dialogWindow, IPosition clonedPosition)
: base(dialogWindow)
{
sourcePosition = clonedPosition;
DisplayName = "Edit Position";
}
public String Symbol
{
get { return sourcePosition.Symbol; }
}
public String CompanyName
{
get
{
CompanyProfile companyProfile = CompanyProfileDA.GetCompanyProfile(sourcePosition.Symbol);
return companyProfile.CompanyName;
}
}
public String PurchaseDate
{
get
{
return sourcePosition.PurchaseDate.ToShortDateString();
}
}
public String PurchasePrice
{
get
{
return Utility.FormatCurrency(sourcePosition.PurchasePrice);
}
set
{
sourcePosition.PurchasePrice = Utility.ParseCurrency(value);
base.OnPropertyChanged("PurchasePrice");
base.OnPropertyChanged("InitialStopRecommendation");
}
}
public String InitialStopLimit
{
get
{
return Utility.FormatCurrency(sourcePosition.InitialStopLimit);
}
set
{
sourcePosition.InitialStopLimit = Utility.ParseCurrency(value);
base.OnPropertyChanged("InitialStopLimit");
if (syncTrailingStop)
{
TrailingStopLimit = Utility.FormatCurrency(sourcePosition.InitialStopLimit);
}
}
}
public bool SyncTrailingStop
{
get
{
return syncTrailingStop;
}
set
{
syncTrailingStop = value;
base.OnPropertyChanged("SyncTrailingStop");
}
}
public String InitialStopRecommendation
{
get
{
return "Recommended Initial Stop: " + Utility.FormatCurrency(sourcePosition.PurchasePrice * (1.00 - sourcePosition.PositionRiskPercentDecimal), 2);
}
}
public String TrailingStopLimit
{
get
{
return Utility.FormatCurrency(sourcePosition.TrailingStopLimit);
}
set
{
if (Utility.ParseCurrency(value).Equals(sourcePosition.TrailingStopLimit)) return;
sourcePosition.TrailingStopLimit = Utility.ParseCurrency(value);
base.OnPropertyChanged("TrailingStopLimit");
}
}
public IPosition GetResult()
{
return sourcePosition;
}
[RelayCommand]
public async Task OkButtonClick()
{
IsSuccess = true;
await Close();
}
[RelayCommand]
public async Task CancelButtonClick()
{
await Close();
}
}
}