Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7fef9b1050 | |||
| 01c2516eaa | |||
| 9a7b09ef7c | |||
| 7e0c5f22b6 | |||
| c5008e33d2 |
@@ -57,6 +57,7 @@ namespace PortfolioManager.Cache
|
|||||||
{
|
{
|
||||||
lock (thisLock)
|
lock (thisLock)
|
||||||
{
|
{
|
||||||
|
DisposeBitmaps();
|
||||||
imageCache = new Dictionary<ImageType, Bitmap>();
|
imageCache = new Dictionary<ImageType, Bitmap>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,22 +67,30 @@ namespace PortfolioManager.Cache
|
|||||||
lock (thisLock)
|
lock (thisLock)
|
||||||
{
|
{
|
||||||
if (null == imageCacheInstance) return;
|
if (null == imageCacheInstance) return;
|
||||||
List<Bitmap> bitmaps = imageCache.Values.ToList();
|
DisposeBitmaps();
|
||||||
foreach (Bitmap bitmap in bitmaps)
|
|
||||||
{
|
|
||||||
bitmap.Dispose();
|
|
||||||
}
|
|
||||||
imageCache = null;
|
imageCache = null;
|
||||||
imageCacheInstance = null;
|
imageCacheInstance = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IImage GetImage(ImageCache.ImageType imageType)
|
private void DisposeBitmaps()
|
||||||
{
|
{
|
||||||
lock(this)
|
if(null == imageCache)return;
|
||||||
|
List<Bitmap> bitmaps = imageCache.Values.ToList();
|
||||||
|
foreach (Bitmap bitmap in bitmaps)
|
||||||
{
|
{
|
||||||
return imageCache[imageType];
|
bitmap.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IImage GetImage(ImageType imageType)
|
||||||
|
{
|
||||||
|
lock(thisLock)
|
||||||
|
{
|
||||||
|
if (imageCache == null)throw new ObjectDisposedException(nameof(ImageCache));
|
||||||
|
if (!imageCache.TryGetValue(imageType, out var bitmap))throw new KeyNotFoundException($"Image {imageType} not found in cache.");
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ namespace PortfolioManager.Cache
|
|||||||
public class SymbolCache : IDisposable
|
public class SymbolCache : IDisposable
|
||||||
{
|
{
|
||||||
private List<String> symbolCache=new List<String>();
|
private List<String> symbolCache=new List<String>();
|
||||||
private Object thisLock=new Object();
|
private readonly Object thisLock=new Object();
|
||||||
|
private readonly Object fetchLock = new Object();
|
||||||
private Thread cacheMonitorThread=null;
|
private Thread cacheMonitorThread=null;
|
||||||
private volatile bool threadRun=true;
|
private volatile bool threadRun=true;
|
||||||
private int cacheRefreshAfter=60000; // Invalidate cache after
|
private int cacheRefreshAfter=60000; // Invalidate cache after
|
||||||
@@ -36,31 +37,42 @@ namespace PortfolioManager.Cache
|
|||||||
symbolCache=new List<string>();
|
symbolCache=new List<string>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
lock(thisLock)
|
lock(thisLock)
|
||||||
{
|
{
|
||||||
if(null==symbolCacheInstance || false==threadRun)return;
|
if(null==symbolCacheInstance || false==threadRun)return;
|
||||||
threadRun=false;
|
threadRun=false;
|
||||||
if(null!=cacheMonitorThread)
|
|
||||||
{
|
|
||||||
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[SymbolCache:Dispose]Thread state is {0}. Joining main thread...",Utility.ThreadStateToString(cacheMonitorThread)));
|
|
||||||
cacheMonitorThread.Join(5000);
|
|
||||||
cacheMonitorThread=null;
|
|
||||||
MDTrace.WriteLine(LogLevel.DEBUG,"[SymbolCache:Dispose] End.");
|
|
||||||
}
|
|
||||||
symbolCacheInstance=null;
|
symbolCacheInstance=null;
|
||||||
}
|
}
|
||||||
|
if(null!=cacheMonitorThread)
|
||||||
|
{
|
||||||
|
MDTrace.WriteLine(LogLevel.DEBUG,String.Format("[SymbolCache:Dispose]Thread state is {0}. Joining main thread...",Utility.ThreadStateToString(cacheMonitorThread)));
|
||||||
|
cacheMonitorThread.Join(5000);
|
||||||
|
cacheMonitorThread=null;
|
||||||
|
MDTrace.WriteLine(LogLevel.DEBUG,"[SymbolCache:Dispose] End.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> GetSymbols()
|
public List<String> GetSymbols()
|
||||||
{
|
{
|
||||||
lock(this)
|
lock(thisLock)
|
||||||
{
|
{
|
||||||
if(0==symbolCache.Count)symbolCache=PricingDA.GetSymbols();
|
if(symbolCache.Count>0)return new List<string>(symbolCache);
|
||||||
return symbolCache;
|
}
|
||||||
|
lock(fetchLock)
|
||||||
|
{
|
||||||
|
List<String> symbols = PricingDA.GetSymbols();
|
||||||
|
lock(thisLock)
|
||||||
|
{
|
||||||
|
if(symbolCache.Count>0)return new List<string>(symbolCache);
|
||||||
|
symbolCache=new List<String>(symbols);
|
||||||
|
return new List<string>(symbols);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThreadProc()
|
private void ThreadProc()
|
||||||
{
|
{
|
||||||
int quantums=0;
|
int quantums=0;
|
||||||
@@ -68,13 +80,15 @@ namespace PortfolioManager.Cache
|
|||||||
while(threadRun)
|
while(threadRun)
|
||||||
{
|
{
|
||||||
Thread.Sleep(quantumInterval);
|
Thread.Sleep(quantumInterval);
|
||||||
|
if(!threadRun)break;
|
||||||
quantums+=quantumInterval;
|
quantums+=quantumInterval;
|
||||||
if(quantums>cacheRefreshAfter)
|
if(quantums>cacheRefreshAfter)
|
||||||
{
|
{
|
||||||
quantums=0;
|
quantums=0;
|
||||||
|
List<String> symbols = PricingDA.GetSymbols();
|
||||||
lock(thisLock)
|
lock(thisLock)
|
||||||
{
|
{
|
||||||
symbolCache=PricingDA.GetSymbols();
|
symbolCache=new List<string>(symbols);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,13 +42,15 @@
|
|||||||
<TextBox Margin="0,4,0,4" Grid.Row="2" Grid.Column="2" IsReadOnly="true" MinWidth="35" Text="{Binding Path=Symbol, Mode=OneWay}"/>
|
<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>
|
<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}"/>
|
<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>
|
<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}" />
|
<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>
|
<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}"/>
|
<TextBox Margin="0,4,0,4" Grid.Row="5" Grid.Column="2" MinWidth="35"
|
||||||
|
Text="{Binding Path=InitialStopLimit, UpdateSourceTrigger=LostFocus, Mode=TwoWay}">
|
||||||
|
</TextBox>
|
||||||
|
|
||||||
<CheckBox Grid.Row="6" Grid.Column="0" IsChecked="{Binding Path=SyncTrailingStop, UpdateSourceTrigger=LostFocus, Mode=TwoWay}">Sync Trailing Stop</CheckBox>
|
<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="7" Grid.Column="0" Grid.ColumnSpan="3" FontWeight="Bold" Content="{Binding Path=InitialStopRecommendation, Mode=OneWay}" />
|
||||||
@@ -56,7 +58,7 @@
|
|||||||
<Label Grid.Row="8" Grid.Column="0">Trailing Stop</Label>
|
<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}"/>
|
<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=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"/>
|
<Button Command="{Binding Path=CancelButtonClick}" Content="_Cancel" MinWidth="70" IsCancel="True" Margin="2" Grid.Row="11" Grid.RowSpan="2" Grid.Column="0"/>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using MarketData.DataAccess;
|
using MarketData.DataAccess;
|
||||||
using MarketData.Generator.Interface;
|
using MarketData.Generator.Interface;
|
||||||
using MarketData.MarketDataModel;
|
using MarketData.MarketDataModel;
|
||||||
|
using MarketData.Numerical;
|
||||||
using MarketData.Utils;
|
using MarketData.Utils;
|
||||||
using PortfolioManager.ViewModels;
|
using PortfolioManager.ViewModels;
|
||||||
|
|
||||||
@@ -92,7 +94,19 @@ namespace PortfolioManager.Dialogs
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return "Recommended Initial Stop: " + Utility.FormatCurrency(sourcePosition.PurchasePrice * (1.00 - sourcePosition.PositionRiskPercentDecimal), 2);
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append("Recommended Initial Stop: ").Append(Utility.FormatCurrency(Numerics.Discount(sourcePosition.PurchasePrice,sourcePosition.PositionRiskPercentDecimal), 2));
|
||||||
|
sb.Append(" = Discount(").Append(Utility.FormatCurrency(sourcePosition.PurchasePrice,2)).Append(",").Append(Utility.FormatPercent(sourcePosition.PositionRiskPercentDecimal,2)).Append(")");
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String InitialStopRecommendationDescription
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return "InitialStopRecommendationDescription";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ namespace PortfolioManager.Models
|
|||||||
{
|
{
|
||||||
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
||||||
if(!Utility.IsEpoch(position.LastStopAdjustment)) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black); // if we have a trailing stop then we are no longer using the initial stop
|
if(!Utility.IsEpoch(position.LastStopAdjustment)) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black); // if we have a trailing stop then we are no longer using the initial stop
|
||||||
StopLimit stopLimit=StopLimitDA.GetStopLimit(position.Symbol);
|
StopLimit stopLimit=StopLimitDA.GetStopLimit(position.Symbol,position.Shares);
|
||||||
if(null==stopLimit||!stopLimit.StopPrice.Equals(Math.Round(position.InitialStopLimit,2))) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Purple);
|
if(null==stopLimit||!stopLimit.StopPrice.Equals(Math.Round(position.InitialStopLimit,2))) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Purple);
|
||||||
if(currentPriceLow<=position.InitialStopLimit) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Red);
|
if(currentPriceLow<=position.InitialStopLimit) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Red);
|
||||||
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
||||||
@@ -295,7 +295,7 @@ namespace PortfolioManager.Models
|
|||||||
{
|
{
|
||||||
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Red);
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Red);
|
||||||
}
|
}
|
||||||
StopLimit stopLimit=StopLimitDA.GetStopLimit(position.Symbol);
|
StopLimit stopLimit=StopLimitDA.GetStopLimit(position.Symbol,position.Shares);
|
||||||
if(null==stopLimit || !stopLimit.StopPrice.Equals(Math.Round(position.TrailingStopLimit,2)))
|
if(null==stopLimit || !stopLimit.StopPrice.Equals(Math.Round(position.TrailingStopLimit,2)))
|
||||||
{
|
{
|
||||||
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Purple);
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Purple);
|
||||||
|
|||||||
@@ -36,17 +36,17 @@ namespace PortfolioManager.Models
|
|||||||
return compositeDataSource;
|
return compositeDataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompositeDataSource CreateCompositeDataSource(DateTime xSource, double ySource)
|
// public static CompositeDataSource CreateCompositeDataSource(DateTime xSource, double ySource)
|
||||||
{
|
// {
|
||||||
if (Utility.IsEpoch(xSource)) return Empty();
|
// if (Utility.IsEpoch(xSource)) return Empty();
|
||||||
SortedDateTimeDataAdapter sortedDateTimeDataAdapter = new SortedDateTimeDataAdapter();
|
// SortedDateTimeDataAdapter sortedDateTimeDataAdapter = new SortedDateTimeDataAdapter();
|
||||||
sortedDateTimeDataAdapter.Add(xSource, ySource);
|
// sortedDateTimeDataAdapter.Add(xSource, ySource);
|
||||||
CompositeDataSource compositeDataSource = new CompositeDataSource()
|
// CompositeDataSource compositeDataSource = new CompositeDataSource()
|
||||||
{
|
// {
|
||||||
DataAdapter = sortedDateTimeDataAdapter
|
// DataAdapter = sortedDateTimeDataAdapter
|
||||||
};
|
// };
|
||||||
return compositeDataSource;
|
// return compositeDataSource;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// This is the active gain/loss as number or percent.
|
// This is the active gain/loss as number or percent.
|
||||||
public static CompositeDataSource GainLoss(ModelPerformanceSeries gainLossList, bool useGainLoss)
|
public static CompositeDataSource GainLoss(ModelPerformanceSeries gainLossList, bool useGainLoss)
|
||||||
|
|||||||
@@ -542,7 +542,7 @@ namespace PortfolioManager.Models
|
|||||||
{
|
{
|
||||||
if (!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
if (!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
||||||
if (!Utility.IsEpoch(position.LastStopAdjustment)) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black); // if we have a trailing stop then we are no longer using the initial stop
|
if (!Utility.IsEpoch(position.LastStopAdjustment)) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black); // if we have a trailing stop then we are no longer using the initial stop
|
||||||
StopLimit stopLimit = StopLimitDA.GetStopLimit(position.Symbol);
|
StopLimit stopLimit = StopLimitDA.GetStopLimit(position.Symbol,position.Shares);
|
||||||
if (null == stopLimit || !stopLimit.StopPrice.Equals(Math.Round(position.InitialStopLimit, 2))) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Purple);
|
if (null == stopLimit || !stopLimit.StopPrice.Equals(Math.Round(position.InitialStopLimit, 2))) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Purple);
|
||||||
if (currentPriceLow <= position.InitialStopLimit) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Red);
|
if (currentPriceLow <= position.InitialStopLimit) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Red);
|
||||||
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
||||||
@@ -569,7 +569,7 @@ namespace PortfolioManager.Models
|
|||||||
{
|
{
|
||||||
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Red);
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Red);
|
||||||
}
|
}
|
||||||
StopLimit stopLimit = StopLimitDA.GetStopLimit(position.Symbol);
|
StopLimit stopLimit = StopLimitDA.GetStopLimit(position.Symbol,position.Shares);
|
||||||
if (null == stopLimit || !stopLimit.StopPrice.Equals(Math.Round(position.TrailingStopLimit, 2))) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Purple);
|
if (null == stopLimit || !stopLimit.StopPrice.Equals(Math.Round(position.TrailingStopLimit, 2))) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Purple);
|
||||||
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Eremex.AvaloniaUI.Charts;
|
using Eremex.AvaloniaUI.Charts;
|
||||||
using MarketData.MarketDataModel;
|
using MarketData.MarketDataModel;
|
||||||
|
using MarketData.Utils;
|
||||||
using PortfolioManager.DataSeriesViewModels;
|
using PortfolioManager.DataSeriesViewModels;
|
||||||
|
|
||||||
namespace PortfolioManager.Models
|
namespace PortfolioManager.Models
|
||||||
@@ -37,5 +38,25 @@ namespace PortfolioManager.Models
|
|||||||
};
|
};
|
||||||
return compositeDataSource;
|
return compositeDataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static CompositeDataSource CreateCompositeDataSource(DateTime xSource,StopLimits stopLimits)
|
||||||
|
{
|
||||||
|
if (null == stopLimits || 0 == stopLimits.Count) return Empty();
|
||||||
|
foreach(StopLimit stopLimit in stopLimits)
|
||||||
|
{
|
||||||
|
stopLimit.EffectiveDate = xSource;
|
||||||
|
}
|
||||||
|
SortedDateTimeDataAdapter sortedDateTimeDataAdapter = new SortedDateTimeDataAdapter();
|
||||||
|
List<StopLimit> sortedStopLimits = stopLimits.OrderBy(x => x.EffectiveDate).ToList();
|
||||||
|
foreach (StopLimit stopLimit in sortedStopLimits)
|
||||||
|
{
|
||||||
|
sortedDateTimeDataAdapter.Add(stopLimit.EffectiveDate, stopLimit.StopPrice);
|
||||||
|
}
|
||||||
|
CompositeDataSource compositeDataSource = new CompositeDataSource()
|
||||||
|
{
|
||||||
|
DataAdapter = sortedDateTimeDataAdapter
|
||||||
|
};
|
||||||
|
return compositeDataSource;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,59 +24,10 @@ using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|||||||
|
|
||||||
namespace PortfolioManager.Renderers
|
namespace PortfolioManager.Renderers
|
||||||
{
|
{
|
||||||
public class MarkerItem
|
/// <summary>
|
||||||
{
|
/// BollingerBandRenderer - Renders the ScottPlot for the Bollinger Bands
|
||||||
public MarkerItem(double markerDate, double markerPrice)
|
/// If external stop limits are provided then the renderer will use those otherwise the renderer will look up using StopLimitDA
|
||||||
{
|
/// </summary>
|
||||||
MarkerDate = markerDate;
|
|
||||||
MarkerPrice = markerPrice;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double MarkerDate { get; set; }
|
|
||||||
|
|
||||||
public double MarkerPrice { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TextMarkerManager : List<MarkerItem>
|
|
||||||
{
|
|
||||||
private DateGenerator dateGenerator = new DateGenerator();
|
|
||||||
private const double DATE_SPREAD_PCNT = 10.0; // PERCENT
|
|
||||||
private const double PRICE_SPREAD_PCNT = 10.0; // PERCENT
|
|
||||||
|
|
||||||
public Coordinates GetBestMarkerLocation(double markerDate, double markerPrice,OffsetDictionary offsetDictionary,double verticalAdjustmentFactor)
|
|
||||||
{
|
|
||||||
if(!IsOverlapped(markerDate,markerPrice,offsetDictionary))
|
|
||||||
{
|
|
||||||
Add(new MarkerItem(markerDate, markerPrice));
|
|
||||||
return new Coordinates(){X=markerDate,Y=markerPrice};
|
|
||||||
}
|
|
||||||
Add(new MarkerItem(markerDate, markerPrice-verticalAdjustmentFactor));
|
|
||||||
return new Coordinates(){X=markerDate,Y=markerPrice-verticalAdjustmentFactor};
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsOverlapped(double markerDate, double markerPrice,OffsetDictionary offsetDictionary)
|
|
||||||
{
|
|
||||||
foreach(MarkerItem markerItem in this)
|
|
||||||
{
|
|
||||||
double markerItemDate = markerItem.MarkerDate;
|
|
||||||
double markerItemPrice = markerItem.MarkerPrice;
|
|
||||||
double minDate = markerItemDate - offsetDictionary.HorizontalSpread*(DATE_SPREAD_PCNT/100.0);
|
|
||||||
double maxDate = markerItemDate + offsetDictionary.HorizontalSpread*(DATE_SPREAD_PCNT/100.0);
|
|
||||||
double minPrice = markerItemPrice - offsetDictionary.VerticalSpread*(PRICE_SPREAD_PCNT/100.0);
|
|
||||||
double maxPrice = markerItemPrice + offsetDictionary.VerticalSpread*(PRICE_SPREAD_PCNT/100.0);
|
|
||||||
|
|
||||||
if(markerDate>=minDate && markerDate<=maxDate && markerPrice >=minPrice && markerPrice<=maxPrice)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// *********************************************************************************************************************************************
|
|
||||||
|
|
||||||
public class BollingerBandRenderer : ModelBase
|
public class BollingerBandRenderer : ModelBase
|
||||||
{
|
{
|
||||||
private String selectedSymbol = default;
|
private String selectedSymbol = default;
|
||||||
@@ -89,8 +40,8 @@ namespace PortfolioManager.Renderers
|
|||||||
private bool showInsiderTransactions = true;
|
private bool showInsiderTransactions = true;
|
||||||
private bool showLeastSquares = true;
|
private bool showLeastSquares = true;
|
||||||
private bool syncTradeToBand = true;
|
private bool syncTradeToBand = true;
|
||||||
private StopLimit stopLimit = default;
|
private StopLimits internalStopLimits = default; // StopLimits that the renderer has located using StopLimitDA
|
||||||
private StopLimits stopLimits = default;
|
private StopLimits externalStopLimits = default; // StopLimits that are provided to the renderer externally by a model
|
||||||
private PortfolioTrades portfolioTrades = default;
|
private PortfolioTrades portfolioTrades = default;
|
||||||
private PortfolioTrades portfolioTradesLots = default;
|
private PortfolioTrades portfolioTradesLots = default;
|
||||||
private Prices prices = default;
|
private Prices prices = default;
|
||||||
@@ -105,6 +56,13 @@ namespace PortfolioManager.Renderers
|
|||||||
PropertyChanged += OnBollingerBandRendererPropertyChanged;
|
PropertyChanged += OnBollingerBandRendererPropertyChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DateTime EarliestBollingerBandDate {get;set;} // This gets set when the bollinger band is generated
|
||||||
|
|
||||||
|
private bool IsVisible(DateTime date)
|
||||||
|
{
|
||||||
|
return syncTradeToBand || date >= EarliestBollingerBandDate;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnBollingerBandRendererPropertyChanged(Object sender, PropertyChangedEventArgs eventArgs)
|
private void OnBollingerBandRendererPropertyChanged(Object sender, PropertyChangedEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (eventArgs.PropertyName.Equals("ShowLegend"))
|
if (eventArgs.PropertyName.Equals("ShowLegend"))
|
||||||
@@ -143,10 +101,12 @@ namespace PortfolioManager.Renderers
|
|||||||
{
|
{
|
||||||
lock(Plotter.Plot.Sync)
|
lock(Plotter.Plot.Sync)
|
||||||
{
|
{
|
||||||
|
int bollingerBandMovingAverageDays = 20;
|
||||||
MDTrace.WriteLine(LogLevel.DEBUG,$"[SetData] ENTER");
|
MDTrace.WriteLine(LogLevel.DEBUG,$"[SetData] ENTER");
|
||||||
this.selectedSymbol = selectedSymbol;
|
this.selectedSymbol = selectedSymbol;
|
||||||
this.selectedDayCount = selectedDayCount;
|
this.selectedDayCount = selectedDayCount;
|
||||||
stopLimit = StopLimitDA.GetStopLimit(selectedSymbol);
|
|
||||||
|
internalStopLimits = StopLimitDA.GetStopLimits(selectedSymbol);
|
||||||
portfolioTrades = PortfolioDA.GetTradesSymbol(selectedSymbol);
|
portfolioTrades = PortfolioDA.GetTradesSymbol(selectedSymbol);
|
||||||
portfolioTradesLots = LotAggregator.CombineLots(portfolioTrades);
|
portfolioTradesLots = LotAggregator.CombineLots(portfolioTrades);
|
||||||
|
|
||||||
@@ -154,13 +114,18 @@ namespace PortfolioManager.Renderers
|
|||||||
if (null != portfolioTrades && 0 != portfolioTrades.Count)
|
if (null != portfolioTrades && 0 != portfolioTrades.Count)
|
||||||
{
|
{
|
||||||
DateGenerator dateGenerator = new DateGenerator();
|
DateGenerator dateGenerator = new DateGenerator();
|
||||||
DateTime earliestTrade = portfolioTrades[0].TradeDate;
|
DateTime earliestTrade = portfolioTrades.First().TradeDate; // portfolio trades are ordered with earliest trade in the lowest index
|
||||||
earliestTrade = earliestTrade.AddDays(-30);
|
earliestTrade = dateGenerator.GenerateHistoricalDate(earliestTrade, bollingerBandMovingAverageDays); // cover the moving average lag in the band
|
||||||
int daysBetween = dateGenerator.DaysBetween(earliestTrade, DateTime.Now);
|
int daysBetween = dateGenerator.DaysBetween(earliestTrade, DateTime.Now);
|
||||||
if (daysBetween < selectedDayCount || !syncTradeToBand) prices = PricingDA.GetPrices(selectedSymbol, selectedDayCount);
|
if (daysBetween < selectedDayCount || !syncTradeToBand)
|
||||||
else prices = PricingDA.GetPrices(selectedSymbol, earliestTrade);
|
{
|
||||||
|
prices = PricingDA.GetPrices(selectedSymbol, selectedDayCount + bollingerBandMovingAverageDays);
|
||||||
DateTime earliestInsiderTransactionDate = dateGenerator.GenerateFutureBusinessDate(prices[prices.Count - 1].Date, 30);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prices = PricingDA.GetPrices(selectedSymbol, earliestTrade);
|
||||||
|
}
|
||||||
|
DateTime earliestInsiderTransactionDate = dateGenerator.GenerateFutureBusinessDate(prices.Last().Date, 30);
|
||||||
insiderTransactionSummaries = InsiderTransactionDA.GetInsiderTransactionSummaries(selectedSymbol, earliestInsiderTransactionDate);
|
insiderTransactionSummaries = InsiderTransactionDA.GetInsiderTransactionSummaries(selectedSymbol, earliestInsiderTransactionDate);
|
||||||
|
|
||||||
// calculate the break even price on the open trades for this symbol
|
// calculate the break even price on the open trades for this symbol
|
||||||
@@ -171,8 +136,8 @@ namespace PortfolioManager.Renderers
|
|||||||
|
|
||||||
if (!syncTradeToBand)
|
if (!syncTradeToBand)
|
||||||
{
|
{
|
||||||
DateTime earliestPricingDate = prices[prices.Count - 1].Date;
|
DateTime earliestPricingDate = prices.Last().Date;
|
||||||
earliestPricingDate = earliestPricingDate.AddDays(30);
|
earliestPricingDate = dateGenerator.GenerateHistoricalDate(earliestPricingDate, bollingerBandMovingAverageDays);
|
||||||
IEnumerable<PortfolioTrade> tradesInRange = (from portfolioTrade in portfolioTradesLots where portfolioTrade.TradeDate >= earliestPricingDate select portfolioTrade);
|
IEnumerable<PortfolioTrade> tradesInRange = (from portfolioTrade in portfolioTradesLots where portfolioTrade.TradeDate >= earliestPricingDate select portfolioTrade);
|
||||||
portfolioTrades = new PortfolioTrades();
|
portfolioTrades = new PortfolioTrades();
|
||||||
foreach (PortfolioTrade portfolioTrade in tradesInRange) portfolioTrades.Add(portfolioTrade);
|
foreach (PortfolioTrade portfolioTrade in tradesInRange) portfolioTrades.Add(portfolioTrade);
|
||||||
@@ -181,15 +146,16 @@ namespace PortfolioManager.Renderers
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
prices = PricingDA.GetPrices(selectedSymbol, selectedDayCount);
|
prices = PricingDA.GetPrices(selectedSymbol, selectedDayCount + bollingerBandMovingAverageDays);
|
||||||
if (null != prices && 0 != prices.Count)
|
if (null != prices && 0 != prices.Count)
|
||||||
{
|
{
|
||||||
DateGenerator dateGenerator = new DateGenerator();
|
DateGenerator dateGenerator = new DateGenerator();
|
||||||
DateTime earliestInsiderTransactionDate = dateGenerator.GenerateFutureBusinessDate(prices[prices.Count - 1].Date, 30);
|
DateTime earliestInsiderTransactionDate = dateGenerator.GenerateFutureBusinessDate(prices.Last().Date, 30);
|
||||||
insiderTransactionSummaries = InsiderTransactionDA.GetInsiderTransactionSummaries(selectedSymbol, earliestInsiderTransactionDate);
|
insiderTransactionSummaries = InsiderTransactionDA.GetInsiderTransactionSummaries(selectedSymbol, earliestInsiderTransactionDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bollingerBands = BollingerBandGenerator.GenerateBollingerBands(prices);
|
bollingerBands = BollingerBandGenerator.GenerateBollingerBands(prices, bollingerBandMovingAverageDays);
|
||||||
|
EarliestBollingerBandDate = bollingerBands.Min(x=>x.Date);
|
||||||
textMarkerManager.Clear();
|
textMarkerManager.Clear();
|
||||||
CalculateOffsets();
|
CalculateOffsets();
|
||||||
GenerateBollingerBands();
|
GenerateBollingerBands();
|
||||||
@@ -284,78 +250,78 @@ namespace PortfolioManager.Renderers
|
|||||||
imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image);
|
imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generate Stop Limits
|
|
||||||
/// </summary>
|
|
||||||
private void GenerateStopLimits()
|
private void GenerateStopLimits()
|
||||||
{
|
{
|
||||||
if (null == stopLimits && null == zeroPrice) return;
|
if (externalStopLimits == null && zeroPrice == null) return;
|
||||||
if (null != stopLimits)
|
|
||||||
{
|
|
||||||
StopLimits = StopLimitCompositeModel.CreateCompositeDataSource(stopLimits);
|
|
||||||
}
|
|
||||||
else if (null != stopLimit && null != zeroPrice)
|
|
||||||
{
|
|
||||||
StopLimits = GainLossModel.CreateCompositeDataSource(zeroPrice.Date, stopLimit.StopPrice);
|
|
||||||
}
|
|
||||||
(DateTime[] dates, double[] values) = StopLimits.ToXYData();
|
|
||||||
|
|
||||||
// Add the image markers
|
// Create the composite data source
|
||||||
Image imageStopLimitMarker = TextMarkerImageGenerator.ToSPImage(ImageCache.GetInstance().GetImage(ImageCache.ImageType.RedTriangleUp));
|
if (externalStopLimits != null)
|
||||||
for (int index = 0; index < dates.Length; index++)
|
StopLimits = StopLimitCompositeModel.CreateCompositeDataSource(externalStopLimits);
|
||||||
{
|
else if (internalStopLimits != null && zeroPrice != null)
|
||||||
DateTime date = dates[index];
|
StopLimits = StopLimitCompositeModel.CreateCompositeDataSource(zeroPrice.Date, internalStopLimits);
|
||||||
double value = values[index];
|
|
||||||
Coordinates coordinates = new Coordinates(date.ToOADate(), value);
|
|
||||||
ImageMarker imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, imageStopLimitMarker, SizeFactor.Normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!showTradeLabels)return;
|
(DateTime[] dates, double[] values) = StopLimits.ToXYData();
|
||||||
|
|
||||||
// Add the text marker
|
// Add the image markers
|
||||||
if (null != stopLimits)
|
Image imageStopLimitMarker = TextMarkerImageGenerator.ToSPImage(
|
||||||
{
|
ImageCache.GetInstance().GetImage(ImageCache.ImageType.RedTriangleUp));
|
||||||
for (int index = 0; index < stopLimits.Count; index++)
|
|
||||||
|
for (int i = 0; i < dates.Length; i++)
|
||||||
{
|
{
|
||||||
StopLimit limit = stopLimits[index];
|
DateTime date = dates[i];
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
if (!IsVisible(date))
|
||||||
sb.Append(limit.StopType).Append(" ");
|
continue;
|
||||||
sb.Append(Utility.FormatCurrency(limit.StopPrice));
|
|
||||||
if (index == stopLimits.Count - 1)
|
|
||||||
{
|
|
||||||
Price latestPrice = prices[0];
|
|
||||||
double percentOffsetFromLow = ((latestPrice.Low - limit.StopPrice) / limit.StopPrice);
|
|
||||||
sb.Append(" (").Append(percentOffsetFromLow > 0 ? "+" : "").Append(Utility.FormatPercent(percentOffsetFromLow)).Append(")");
|
|
||||||
}
|
|
||||||
Image image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), FontFactor.FontSize);
|
|
||||||
Coordinates coordinates = new Coordinates(limit.EffectiveDate.ToOADate(),
|
|
||||||
limit.StopPrice - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset6P5PC));
|
|
||||||
|
|
||||||
coordinates = textMarkerManager.GetBestMarkerLocation(coordinates.X, coordinates.Y,offsets, offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset6P5PC));
|
double value = values[i];
|
||||||
|
Coordinates coordinates = new Coordinates(date.ToOADate(), value);
|
||||||
ImageMarker imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image);
|
Plotter.Plot.Add.ImageMarker(coordinates, imageStopLimitMarker, SizeFactor.Normal);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
if (!showTradeLabels) return;
|
||||||
{
|
|
||||||
if (null == zeroPrice) return;
|
|
||||||
if (null == stopLimit || null == zeroPrice) return;
|
|
||||||
Price latestPrice = prices[0];
|
Price latestPrice = prices[0];
|
||||||
double percentOffsetFromLow = ((latestPrice.Low - stopLimit.StopPrice) / stopLimit.StopPrice);
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
// Helper to draw text markers
|
||||||
sb.Append(stopLimit.StopType).Append(" ");
|
void DrawTextMarker(StopLimit limit, bool isLast)
|
||||||
sb.Append(Utility.FormatCurrency(stopLimit.StopPrice));
|
{
|
||||||
sb.Append(" (").Append(percentOffsetFromLow > 0 ? "+" : "").Append(Utility.FormatPercent(percentOffsetFromLow)).Append(")");
|
if (!IsVisible(limit.EffectiveDate)) return;
|
||||||
Image image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), FontFactor.FontSize);
|
|
||||||
Coordinates coordinates = new Coordinates(latestPrice.Date.ToOADate() - offsets.Offset(OffsetDictionary.OffsetType.HorizontalOffset3PC),
|
|
||||||
stopLimit.StopPrice - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset6P5PC));
|
|
||||||
|
|
||||||
coordinates = textMarkerManager.GetBestMarkerLocation(coordinates.X, coordinates.Y,offsets, offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset6P5PC));
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append(limit.StopType).Append(" ");
|
||||||
|
sb.Append(Utility.FormatCurrency(limit.StopPrice));
|
||||||
|
|
||||||
ImageMarker imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image);
|
if (isLast)
|
||||||
}
|
{
|
||||||
|
double percentOffsetFromLow = (latestPrice.Low - limit.StopPrice) / limit.StopPrice;
|
||||||
|
sb.Append(" (")
|
||||||
|
.Append(percentOffsetFromLow > 0 ? "+" : "")
|
||||||
|
.Append(Utility.FormatPercent(percentOffsetFromLow))
|
||||||
|
.Append(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
Image image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), FontFactor.FontSize);
|
||||||
|
Coordinates coordinates = new Coordinates(
|
||||||
|
limit.EffectiveDate.ToOADate(),
|
||||||
|
limit.StopPrice - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset6P5PC));
|
||||||
|
|
||||||
|
coordinates = textMarkerManager.GetBestMarkerLocation(
|
||||||
|
coordinates.X, coordinates.Y, offsets, offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset6P5PC));
|
||||||
|
|
||||||
|
Plotter.Plot.Add.ImageMarker(coordinates, image);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw external or internal stop limits
|
||||||
|
if (externalStopLimits != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < externalStopLimits.Count; i++)
|
||||||
|
DrawTextMarker(externalStopLimits[i], i == externalStopLimits.Count - 1);
|
||||||
|
}
|
||||||
|
else if (internalStopLimits != null && zeroPrice != null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < internalStopLimits.Count; i++)
|
||||||
|
DrawTextMarker(internalStopLimits[i], i == internalStopLimits.Count - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -363,39 +329,55 @@ namespace PortfolioManager.Renderers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void GenerateTradePoints()
|
private void GenerateTradePoints()
|
||||||
{
|
{
|
||||||
if (null == portfolioTradesLots || 0 == portfolioTradesLots.Count) return;
|
if (portfolioTradesLots == null || portfolioTradesLots.Count == 0) return;
|
||||||
|
|
||||||
// Here we add the image markers
|
// Add the image markers
|
||||||
Image tradePointMarker = TextMarkerImageGenerator.ToSPImage(ImageCache.GetInstance().GetImage(ImageCache.ImageType.YellowTriangleUp));
|
Image tradePointMarker = TextMarkerImageGenerator.ToSPImage(
|
||||||
for (int index = 0; index < portfolioTradesLots.Count; index++)
|
ImageCache.GetInstance().GetImage(ImageCache.ImageType.YellowTriangleUp));
|
||||||
{
|
|
||||||
PortfolioTrade portfolioTrade = portfolioTradesLots[index];
|
|
||||||
Coordinates coordinates = new Coordinates(portfolioTrade.TradeDate.ToOADate(), portfolioTrade.Price);
|
|
||||||
ImageMarker imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, tradePointMarker, SizeFactor.Normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showTradeLabels)
|
foreach (var portfolioTrade in portfolioTradesLots)
|
||||||
{
|
|
||||||
// This adds the text markers
|
|
||||||
for (int index = 0; index < portfolioTradesLots.Count; index++)
|
|
||||||
{
|
{
|
||||||
PortfolioTrade portfolioTrade = portfolioTradesLots[index];
|
if (!IsVisible(portfolioTrade.TradeDate))
|
||||||
StringBuilder sb = new StringBuilder();
|
continue;
|
||||||
sb.Append(portfolioTrade.BuySell.Equals("B") ? "Buy " : "Sell ");
|
|
||||||
sb.Append(Utility.FormatNumber(portfolioTrade.Shares));
|
|
||||||
sb.Append("@");
|
|
||||||
sb.Append(Utility.FormatCurrency(portfolioTrade.Price));
|
|
||||||
Image image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), FontFactor.FontSize);
|
|
||||||
Coordinates coordinates = new Coordinates(portfolioTrade.TradeDate.ToOADate(),
|
|
||||||
portfolioTrade.Price - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset6P5PC));
|
|
||||||
|
|
||||||
coordinates = textMarkerManager.GetBestMarkerLocation(coordinates.X, coordinates.Y,offsets, offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset6P5PC));
|
Coordinates coordinates = new Coordinates(
|
||||||
|
portfolioTrade.TradeDate.ToOADate(),
|
||||||
|
portfolioTrade.Price);
|
||||||
|
|
||||||
ImageMarker imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image);
|
ImageMarker imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, tradePointMarker, SizeFactor.Normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!showTradeLabels) return;
|
||||||
|
|
||||||
|
// Add the text markers
|
||||||
|
foreach (var portfolioTrade in portfolioTradesLots)
|
||||||
|
{
|
||||||
|
if (!IsVisible(portfolioTrade.TradeDate))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.Append(portfolioTrade.BuySell.Equals("B") ? "Buy " : "Sell ");
|
||||||
|
sb.Append(Utility.FormatNumber(portfolioTrade.Shares));
|
||||||
|
sb.Append("@");
|
||||||
|
sb.Append(Utility.FormatCurrency(portfolioTrade.Price));
|
||||||
|
|
||||||
|
Image image = TextMarkerImageGenerator.GenerateImage(sb.ToString(), FontFactor.FontSize);
|
||||||
|
|
||||||
|
Coordinates coordinates = new Coordinates(
|
||||||
|
portfolioTrade.TradeDate.ToOADate(),
|
||||||
|
portfolioTrade.Price - offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset6P5PC));
|
||||||
|
|
||||||
|
coordinates = textMarkerManager.GetBestMarkerLocation(
|
||||||
|
coordinates.X,
|
||||||
|
coordinates.Y,
|
||||||
|
offsets,
|
||||||
|
offsets.Offset(OffsetDictionary.OffsetType.VerticalOffset6P5PC));
|
||||||
|
|
||||||
|
ImageMarker imageMarker = Plotter.Plot.Add.ImageMarker(coordinates, image);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generate Insider Transactions
|
/// Generate Insider Transactions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -410,8 +392,8 @@ namespace PortfolioManager.Renderers
|
|||||||
// get the maximum date in the bollinger band series
|
// get the maximum date in the bollinger band series
|
||||||
DateTime maxBollingerDate = (from BollingerBandElement bollingerBandElement in bollingerBands select bollingerBandElement.Date).Max();
|
DateTime maxBollingerDate = (from BollingerBandElement bollingerBandElement in bollingerBands select bollingerBandElement.Date).Max();
|
||||||
// ensure that the insider transactions are clipped to the bollingerband max date. There are some items in insider transaction summaries (options dated in the future) that will throw the graphic out of proportion
|
// ensure that the insider transactions are clipped to the bollingerband max date. There are some items in insider transaction summaries (options dated in the future) that will throw the graphic out of proportion
|
||||||
InsiderTransactionSummaries disposedSummaries = new InsiderTransactionSummaries((from InsiderTransactionSummary insiderTransactionSummary in insiderTransactionSummaries where insiderTransactionSummary.NumberOfSharesAcquiredDisposed < 0 && insiderTransactionSummary.TransactionDate.Date <= maxBollingerDate select insiderTransactionSummary).ToList());
|
InsiderTransactionSummaries disposedSummaries = new InsiderTransactionSummaries((from InsiderTransactionSummary insiderTransactionSummary in insiderTransactionSummaries where insiderTransactionSummary.NumberOfSharesAcquiredDisposed < 0 && insiderTransactionSummary.TransactionDate.Date <= maxBollingerDate && IsVisible(insiderTransactionSummary.TransactionDate.Date) select insiderTransactionSummary).ToList());
|
||||||
InsiderTransactionSummaries acquiredSummaries = new InsiderTransactionSummaries((from InsiderTransactionSummary insiderTransactionSummary in insiderTransactionSummaries where insiderTransactionSummary.NumberOfSharesAcquiredDisposed > 0 && insiderTransactionSummary.TransactionDate.Date <= maxBollingerDate select insiderTransactionSummary).ToList());
|
InsiderTransactionSummaries acquiredSummaries = new InsiderTransactionSummaries((from InsiderTransactionSummary insiderTransactionSummary in insiderTransactionSummaries where insiderTransactionSummary.NumberOfSharesAcquiredDisposed > 0 && insiderTransactionSummary.TransactionDate.Date <= maxBollingerDate && IsVisible(insiderTransactionSummary.TransactionDate.Date) select insiderTransactionSummary).ToList());
|
||||||
|
|
||||||
BinCollection<InsiderTransactionSummary> disposedSummariesBin = BinHelper<InsiderTransactionSummary>.CreateBins(new BinItems<InsiderTransactionSummary>(disposedSummaries), 3);
|
BinCollection<InsiderTransactionSummary> disposedSummariesBin = BinHelper<InsiderTransactionSummary>.CreateBins(new BinItems<InsiderTransactionSummary>(disposedSummaries), 3);
|
||||||
BinCollection<InsiderTransactionSummary> acquiredSummariesBin = BinHelper<InsiderTransactionSummary>.CreateBins(new BinItems<InsiderTransactionSummary>(acquiredSummaries), 3);
|
BinCollection<InsiderTransactionSummary> acquiredSummariesBin = BinHelper<InsiderTransactionSummary>.CreateBins(new BinItems<InsiderTransactionSummary>(acquiredSummaries), 3);
|
||||||
@@ -573,11 +555,11 @@ namespace PortfolioManager.Renderers
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return stopLimits;
|
return externalStopLimits;
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
stopLimits = value;
|
externalStopLimits = value;
|
||||||
base.OnPropertyChanged("ExternalStopLimits");
|
base.OnPropertyChanged("ExternalStopLimits");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
PortfolioManager/Renderers/MarkerItem.cs
Normal file
16
PortfolioManager/Renderers/MarkerItem.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
namespace PortfolioManager.Renderers
|
||||||
|
{
|
||||||
|
public class MarkerItem
|
||||||
|
{
|
||||||
|
public MarkerItem(double markerDate, double markerPrice)
|
||||||
|
{
|
||||||
|
MarkerDate = markerDate;
|
||||||
|
MarkerPrice = markerPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double MarkerDate { get; set; }
|
||||||
|
|
||||||
|
public double MarkerPrice { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
43
PortfolioManager/Renderers/TextMarkerManager.cs
Normal file
43
PortfolioManager/Renderers/TextMarkerManager.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using MarketData.Utils;
|
||||||
|
using ScottPlot;
|
||||||
|
|
||||||
|
namespace PortfolioManager.Renderers
|
||||||
|
{
|
||||||
|
public class TextMarkerManager : List<MarkerItem>
|
||||||
|
{
|
||||||
|
private DateGenerator dateGenerator = new DateGenerator();
|
||||||
|
private const double DATE_SPREAD_PCNT = 10.0; // PERCENT
|
||||||
|
private const double PRICE_SPREAD_PCNT = 10.0; // PERCENT
|
||||||
|
|
||||||
|
public Coordinates GetBestMarkerLocation(double markerDate, double markerPrice,OffsetDictionary offsetDictionary,double verticalAdjustmentFactor)
|
||||||
|
{
|
||||||
|
if(!IsOverlapped(markerDate,markerPrice,offsetDictionary))
|
||||||
|
{
|
||||||
|
Add(new MarkerItem(markerDate, markerPrice));
|
||||||
|
return new Coordinates(){X=markerDate,Y=markerPrice};
|
||||||
|
}
|
||||||
|
Add(new MarkerItem(markerDate, markerPrice-verticalAdjustmentFactor));
|
||||||
|
return new Coordinates(){X=markerDate,Y=markerPrice-verticalAdjustmentFactor};
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsOverlapped(double markerDate, double markerPrice,OffsetDictionary offsetDictionary)
|
||||||
|
{
|
||||||
|
foreach(MarkerItem markerItem in this)
|
||||||
|
{
|
||||||
|
double markerItemDate = markerItem.MarkerDate;
|
||||||
|
double markerItemPrice = markerItem.MarkerPrice;
|
||||||
|
double minDate = markerItemDate - offsetDictionary.HorizontalSpread*(DATE_SPREAD_PCNT/100.0);
|
||||||
|
double maxDate = markerItemDate + offsetDictionary.HorizontalSpread*(DATE_SPREAD_PCNT/100.0);
|
||||||
|
double minPrice = markerItemPrice - offsetDictionary.VerticalSpread*(PRICE_SPREAD_PCNT/100.0);
|
||||||
|
double maxPrice = markerItemPrice + offsetDictionary.VerticalSpread*(PRICE_SPREAD_PCNT/100.0);
|
||||||
|
|
||||||
|
if(markerDate>=minDate && markerDate<=maxDate && markerPrice >=minPrice && markerPrice<=maxPrice)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,6 @@
|
|||||||
Type,PortfolioManager.ViewModels.MGSHMomentumViewModel,PathFileName,C:\boneyard\marketdata\Sessions\MGSH20250331.TXT
|
Type,PortfolioManager.ViewModels.MGSHMomentumViewModel,PathFileName,C:\boneyard\marketdata\Sessions\MGSH20250331.TXT
|
||||||
Type,PortfolioManager.ViewModels.MomentumViewModel,PathFileName,C:\boneyard\marketdata\Sessions\MG20180131.TXT
|
Type,PortfolioManager.ViewModels.MomentumViewModel,PathFileName,C:\boneyard\marketdata\Sessions\MG20180131.TXT
|
||||||
Type,PortfolioManager.ViewModels.CMMomentumViewModel,PathFileName,C:\boneyard\marketdata\Sessions\CM20191031.TXT
|
Type,PortfolioManager.ViewModels.CMMomentumViewModel,PathFileName,C:\boneyard\marketdata\Sessions\CM20191031.TXT
|
||||||
Type,PortfolioManager.ViewModels.CMTrendViewModel,PathFileName,C:\boneyard\marketdata\Sessions\CMT20200817.TXT
|
Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,ALHC,SelectedWatchList,Valuations,SelectedDayCount,90,SyncTradeToBand,True,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True
|
||||||
Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,SPOT,SelectedWatchList,Valuations,SelectedDayCount,90,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True
|
Type,PortfolioManager.ViewModels.CMTrendViewModel,PathFileName,C:\boneyard\marketdata\bin\Debug\saferun\CMT20200817.TXT
|
||||||
Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,GWRE,SelectedWatchList,Valuations,SelectedDayCount,90,SyncTradeToBand,True,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True
|
Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,HWM,SelectedWatchList,Valuations,SelectedDayCount,90,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True,StopHistoryCount,5,StopHistory_0,Symbol=HWM|StopPrice=176.71|Shares=0|StopType=Stop Quote|EffectiveDate=11/17/2025,StopHistory_1,Symbol=HWM|StopPrice=186.778498840332|Shares=0|StopType=Stop Quote|EffectiveDate=11/26/2025,StopHistory_2,Symbol=HWM|StopPrice=193.123001594543|Shares=0|StopType=Stop Quote|EffectiveDate=12/26/2025,StopHistory_3,Symbol=HWM|StopPrice=197.56385799408|Shares=0|StopType=Stop Quote|EffectiveDate=1/26/2026,StopHistory_4,Symbol=HWM|StopPrice=235.233001537323|Shares=0|StopType=Stop Quote|EffectiveDate=2/25/2026
|
||||||
Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,CRS,SelectedWatchList,Valuations,SelectedDayCount,90,SyncTradeToBand,True,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True
|
|
||||||
Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,T,SelectedWatchList,Valuations,SelectedDayCount,90,SyncTradeToBand,True,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True
|
|
||||||
Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,NRG,SelectedWatchList,Valuations,SelectedDayCount,90,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True
|
|
||||||
Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,PSO,SelectedWatchList,Valuations,SelectedDayCount,90,SyncTradeToBand,True,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True
|
|
||||||
Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,PARR,SelectedWatchList,Valuations,SelectedDayCount,180,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True
|
|
||||||
Type,PortfolioManager.ViewModels.BollingerBandViewModel,SelectedSymbol,SH,SelectedWatchList,Valuations,SelectedDayCount,90,SyncTradeToBand,False,ShowTradeLabels,True,UseLeastSquaresFit,True,ShowInsiderTransactions,True
|
|
||||||
|
|||||||
Reference in New Issue
Block a user