507 lines
18 KiB
C#
507 lines
18 KiB
C#
using MarketData.Generator;
|
|
using MarketData.MarketDataModel;
|
|
using MarketData.Utils;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Media;
|
|
using TradeBlotter.ViewModels;
|
|
using TradeBlotter.UIUtils;
|
|
using System.Collections.Specialized;
|
|
using MarketData.Generator.CMTrend;
|
|
using MarketData;
|
|
|
|
using Position=MarketData.Generator.CMTrend.Position;
|
|
using StopLimit=MarketData.MarketDataModel.StopLimit;
|
|
|
|
using MarketData.DataAccess;
|
|
using TradeBlotter.Interface;
|
|
|
|
namespace TradeBlotter.Model
|
|
{
|
|
public class CMTPositionModelCollection:ObservableCollection<CMTPositionModel>
|
|
{
|
|
public void Add(ActivePositions activePositions)
|
|
{
|
|
foreach(Position position in activePositions)Add(new CMTPositionModel(position));
|
|
}
|
|
public void Add(Positions allPositions)
|
|
{
|
|
foreach(Position position in allPositions) Add(new CMTPositionModel(position));
|
|
}
|
|
public void OnCollectionChanged()
|
|
{
|
|
base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset,null));
|
|
}
|
|
}
|
|
|
|
public class CMTPositionModel : ModelBase, IPositionModel
|
|
{
|
|
private Position position=new Position();
|
|
private int priceChangeDirection=0;
|
|
private double prevPrice=double.NaN;
|
|
private double currentPriceLow=double.NaN;
|
|
private DateTime lastUpdated=DateTime.Now;
|
|
private double edgeRatio=double.NaN;
|
|
|
|
public CMTPositionModel()
|
|
{
|
|
}
|
|
public CMTPositionModel(Position position)
|
|
{
|
|
Symbol=position.Symbol;
|
|
PurchaseDate=position.PurchaseDate;
|
|
SellDate=position.SellDate;
|
|
Shares=position.Shares;
|
|
PurchasePrice=position.PurchasePrice;
|
|
CurrentPrice=position.CurrentPrice;
|
|
PositionRiskPercentDecimal=position.PositionRiskPercentDecimal;
|
|
InitialStopLimit=position.InitialStopLimit;
|
|
TrailingStopLimit=position.TrailingStopLimit;
|
|
LastStopAdjustment=position.LastStopAdjustment;
|
|
this.position.R=position.R;
|
|
this.position.C=position.C;
|
|
}
|
|
public void UpdateProperties()
|
|
{
|
|
base.OnPropertyChanged("CurrentPrice");
|
|
base.OnPropertyChanged("MarketValue");
|
|
base.OnPropertyChanged("ActiveMarketValue");
|
|
base.OnPropertyChanged("GainLoss");
|
|
base.OnPropertyChanged("GainLossPcnt");
|
|
// base.OnPropertyChanged("RMultipleAsString");
|
|
base.OnPropertyChanged("RMultiple");
|
|
base.OnPropertyChanged("EdgeRatioAsString");
|
|
|
|
base.OnPropertyChanged("CurrentPriceColor");
|
|
base.OnPropertyChanged("TrailingStopLimitColor");
|
|
base.OnPropertyChanged("InitialStopLimitColor");
|
|
base.OnPropertyChanged("TotalRiskExposureColor");
|
|
base.OnPropertyChanged("ActiveMarketValueColor");
|
|
base.OnPropertyChanged("GainLossColor");
|
|
base.OnPropertyChanged("GainLossPcntColor");
|
|
base.OnPropertyChanged("EdgeRatioAsStringColor");
|
|
base.OnPropertyChanged("RMultipleColor");
|
|
}
|
|
public Position Position
|
|
{
|
|
get{return position;}
|
|
set
|
|
{
|
|
Symbol=position.Symbol;
|
|
PurchaseDate=position.PurchaseDate;
|
|
SellDate=position.SellDate;
|
|
Shares=position.Shares;
|
|
PurchasePrice=position.PurchasePrice;
|
|
CurrentPrice=position.CurrentPrice;
|
|
PositionRiskPercentDecimal=position.PositionRiskPercentDecimal;
|
|
InitialStopLimit=position.InitialStopLimit;
|
|
TrailingStopLimit=position.TrailingStopLimit;
|
|
LastStopAdjustment=position.LastStopAdjustment;
|
|
this.position.R=position.R;
|
|
this.position.C=position.C;
|
|
}
|
|
}
|
|
public String Symbol
|
|
{
|
|
get { return position.Symbol; }
|
|
set { position.Symbol=value; base.OnPropertyChanged("Symbol"); }
|
|
}
|
|
public Brush SymbolColor
|
|
{
|
|
get
|
|
{
|
|
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
|
}
|
|
}
|
|
// ***
|
|
public DateTime PurchaseDate
|
|
{
|
|
get { return position.PurchaseDate; }
|
|
set { position.PurchaseDate=value; base.OnPropertyChanged("PurchaseDate"); }
|
|
}
|
|
|
|
public Brush PurchaseDateColor
|
|
{
|
|
get
|
|
{
|
|
DateGenerator dateGenerator=new DateGenerator();
|
|
if(!dateGenerator.IsMarketOpen(PurchaseDate)) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Red);
|
|
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
|
}
|
|
}
|
|
|
|
public DateTime SellDate
|
|
{
|
|
get { return position.SellDate; }
|
|
set { position.SellDate=value; base.OnPropertyChanged("SellDate"); }
|
|
}
|
|
|
|
public Brush SellDateColor
|
|
{
|
|
get
|
|
{
|
|
if(Utility.IsEpoch(SellDate)) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
|
DateGenerator dateGenerator=new DateGenerator();
|
|
if(!dateGenerator.IsMarketOpen(SellDate)) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Red);
|
|
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
|
}
|
|
}
|
|
// ***
|
|
public int DaysHeld
|
|
{
|
|
get
|
|
{
|
|
DateGenerator dateGenerator=new DateGenerator();
|
|
if(Utility.IsEpoch(SellDate)) return dateGenerator.DaysBetween(PurchaseDate,DateTime.Now.Date);
|
|
return dateGenerator.DaysBetween(PurchaseDate,SellDate);
|
|
}
|
|
}
|
|
public Brush DaysHeldColor
|
|
{
|
|
get
|
|
{
|
|
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
|
}
|
|
}
|
|
// ***
|
|
// Here we take R as based upon our StopLimit.
|
|
public double R
|
|
{
|
|
get
|
|
{
|
|
if(position.TrailingStopLimit>position.PurchasePrice)return 0.00; // here we are considering the current risk/share which is based on our stop limit
|
|
return position.TrailingStopLimit>position.PurchasePrice?0.00:position.PurchasePrice-position.TrailingStopLimit;
|
|
}
|
|
}
|
|
public Brush RColor
|
|
{
|
|
get
|
|
{
|
|
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
|
}
|
|
}
|
|
// ***
|
|
public double TotalRiskExposure
|
|
{
|
|
get{return R*position.Shares;}
|
|
}
|
|
public Brush TotalRiskExposureColor
|
|
{
|
|
get
|
|
{
|
|
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
|
}
|
|
}
|
|
// ***
|
|
public double EdgeRatio
|
|
{
|
|
get{return edgeRatio;}
|
|
set
|
|
{
|
|
edgeRatio=value;
|
|
base.OnPropertyChanged("EdgeRatio");
|
|
base.OnPropertyChanged("EdgeRatioAsStringColor");
|
|
}
|
|
}
|
|
public String EdgeRatioAsString
|
|
{
|
|
get{return Utility.FormatNumber(edgeRatio,2,false);}
|
|
}
|
|
public Brush EdgeRatioAsStringColor
|
|
{
|
|
get
|
|
{
|
|
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
|
if(double.IsNaN(edgeRatio))return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
|
if(edgeRatio>=1.00)return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Green);
|
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Red);
|
|
}
|
|
}
|
|
// ***
|
|
|
|
public String RMultipleAsString
|
|
{
|
|
get{return Utility.FormatNumber((position.CurrentPrice-position.PurchasePrice)/(position.PurchasePrice-position.InitialStopLimit),2,false)+"R";} // always based on original position risk
|
|
}
|
|
|
|
public double RMultiple
|
|
{
|
|
get{return (position.CurrentPrice-position.PurchasePrice)/(position.PurchasePrice-position.InitialStopLimit);} // always based on original position risk
|
|
}
|
|
|
|
public Brush RMultipleColor
|
|
{
|
|
get
|
|
{
|
|
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
|
}
|
|
}
|
|
// ***
|
|
public double PositionRiskPercentDecimal
|
|
{
|
|
get{return position.PositionRiskPercentDecimal;}
|
|
set { position.PositionRiskPercentDecimal=value; base.OnPropertyChanged("PositionRiskPercentDecimal"); }
|
|
}
|
|
public double InitialStopLimit
|
|
{
|
|
get { return position.InitialStopLimit; }
|
|
set
|
|
{
|
|
position.InitialStopLimit=value;
|
|
base.OnPropertyChanged("InitialStopLimit");
|
|
base.OnPropertyChanged("InitialStopLimitColor");
|
|
}
|
|
}
|
|
public Brush InitialStopLimitColor
|
|
{
|
|
get
|
|
{
|
|
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
|
|
StopLimit stopLimit=PortfolioDA.GetStopLimit(position.Symbol);
|
|
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);
|
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
|
}
|
|
}
|
|
public double TrailingStopLimit
|
|
{
|
|
get { return position.TrailingStopLimit; }
|
|
set
|
|
{
|
|
position.TrailingStopLimit=value;
|
|
base.OnPropertyChanged("TrailingStopLimit");
|
|
base.OnPropertyChanged("TrailingStopLimitColor");
|
|
}
|
|
}
|
|
public Brush TrailingStopLimitColor
|
|
{
|
|
get
|
|
{
|
|
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
|
StopLimit stopLimit=PortfolioDA.GetStopLimit(position.Symbol);
|
|
if(null==stopLimit||!stopLimit.StopPrice.Equals(Math.Round(position.TrailingStopLimit,2))) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Purple);
|
|
if(currentPriceLow<=position.TrailingStopLimit)return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Red);
|
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
|
}
|
|
}
|
|
// ***
|
|
public DateTime LastStopAdjustment
|
|
{
|
|
get { return position.LastStopAdjustment; }
|
|
set { position.LastStopAdjustment=value; base.OnPropertyChanged("LastStopAdjustment"); }
|
|
}
|
|
public Brush LastStopAdjustmentColor
|
|
{
|
|
get
|
|
{
|
|
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
|
}
|
|
}
|
|
// ***
|
|
public int DaysSinceLastStopAdjustment
|
|
{
|
|
get
|
|
{
|
|
if(!IsActivePosition)return int.MinValue;
|
|
DateGenerator dateGenerator=new DateGenerator();
|
|
DateTime today=DateTime.Now;
|
|
int daysSinceLastStopAdjustment=int.MinValue;
|
|
if(Utility.IsEpoch(position.LastStopAdjustment)) daysSinceLastStopAdjustment=dateGenerator.DaysBetweenActual(today,position.PurchaseDate);
|
|
else daysSinceLastStopAdjustment=dateGenerator.DaysBetweenActual(today,position.LastStopAdjustment);
|
|
return daysSinceLastStopAdjustment;
|
|
}
|
|
}
|
|
public Brush DaysSinceLastStopAdjustmentColor
|
|
{
|
|
get
|
|
{
|
|
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
|
int daysSinceLastStopAdjustment=DaysSinceLastStopAdjustment;
|
|
if(int.MinValue.Equals(daysSinceLastStopAdjustment))return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
|
if(daysSinceLastStopAdjustment>=90) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Red);
|
|
else if(daysSinceLastStopAdjustment>=60) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Red); // I made them both red because yellow and purple looked horrible
|
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Green);
|
|
}
|
|
}
|
|
|
|
// ***
|
|
public double Shares
|
|
{
|
|
get { return position.Shares; }
|
|
set { position.Shares=value; base.OnPropertyChanged("Shares"); base.OnPropertyChanged("Exposure"); base.OnPropertyChanged("ActiveExposure"); base.OnPropertyChanged("MarketValue"); base.OnPropertyChanged("ActiveMarketValue"); base.OnPropertyChanged("GainLoss"); base.OnPropertyChanged("GainLossPcnt"); }
|
|
}
|
|
public Brush SharesColor
|
|
{
|
|
get
|
|
{
|
|
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
|
}
|
|
}
|
|
// ***
|
|
public double PurchasePrice
|
|
{
|
|
get { return position.PurchasePrice; }
|
|
set { position.PurchasePrice=value; base.OnPropertyChanged("PurchasePrice"); base.OnPropertyChanged("Exposure"); base.OnPropertyChanged("ActiveExposure"); base.OnPropertyChanged("GainLoss"); base.OnPropertyChanged("GainLossPcnt"); }
|
|
}
|
|
public Brush PurchasePriceColor
|
|
{
|
|
get
|
|
{
|
|
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
|
}
|
|
}
|
|
// ***
|
|
public double CurrentPrice
|
|
{
|
|
get { return position.CurrentPrice; }
|
|
set
|
|
{
|
|
if(position.CurrentPrice==value) return;
|
|
position.CurrentPrice=value;
|
|
if(double.IsNaN(prevPrice)) { prevPrice=position.CurrentPrice; priceChangeDirection=0; }
|
|
else if(prevPrice>position.CurrentPrice) priceChangeDirection=-1;
|
|
else if(prevPrice==position.CurrentPrice) priceChangeDirection=0;
|
|
else priceChangeDirection=1;
|
|
prevPrice=position.CurrentPrice;
|
|
UpdateProperties();
|
|
}
|
|
}
|
|
public Brush CurrentPriceColor
|
|
{
|
|
get
|
|
{
|
|
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
|
if(priceChangeDirection>0) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Green);
|
|
else if(priceChangeDirection<0) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Red);
|
|
else return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
|
}
|
|
}
|
|
// ***
|
|
|
|
public double CurrentPriceLow
|
|
{
|
|
get{return currentPriceLow;}
|
|
set
|
|
{
|
|
if(currentPriceLow==value)return;
|
|
currentPriceLow=value;
|
|
base.OnPropertyChanged("CurrentPriceLow");
|
|
base.OnPropertyChanged("CurrentPriceLowAsString");
|
|
base.OnPropertyChanged("TrailingStopLimitColor");
|
|
base.OnPropertyChanged("InitialStopLimitColor");
|
|
}
|
|
}
|
|
// ***
|
|
public String CurrentPriceLowAsString
|
|
{
|
|
get { return Utility.FormatCurrency(currentPriceLow); }
|
|
}
|
|
public Brush CurrentPriceLowAsStringColor
|
|
{
|
|
get
|
|
{
|
|
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
|
}
|
|
}
|
|
// ***
|
|
public DateTime LastUpdated
|
|
{
|
|
get { return lastUpdated; }
|
|
set { lastUpdated=value; base.OnPropertyChanged("LastUpdated"); }
|
|
}
|
|
public Brush LastUpdatedColor
|
|
{
|
|
get
|
|
{
|
|
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
|
return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
|
}
|
|
}
|
|
// ***
|
|
|
|
public double Exposure
|
|
{
|
|
get { return Shares*PurchasePrice; }
|
|
}
|
|
public double ActiveExposure
|
|
{
|
|
get { return IsActivePosition?Exposure:0.00; }
|
|
}
|
|
public Brush ActiveExposureColor
|
|
{
|
|
get
|
|
{
|
|
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
|
else return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
|
}
|
|
}
|
|
public double MarketValue
|
|
{
|
|
get { return Shares*CurrentPrice; }
|
|
}
|
|
public double ActiveMarketValue
|
|
{
|
|
get { return IsActivePosition?MarketValue:0.00; }
|
|
}
|
|
public Brush ActiveMarketValueColor
|
|
{
|
|
get
|
|
{
|
|
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
|
if(ActiveMarketValue>Exposure) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Green);
|
|
else if(ActiveMarketValue<Exposure) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Red);
|
|
else return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
|
}
|
|
}
|
|
public double GainLoss
|
|
{
|
|
get { return MarketValue-Exposure; }
|
|
}
|
|
public Brush GainLossColor
|
|
{
|
|
get
|
|
{
|
|
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
|
if(GainLoss>0) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Green);
|
|
else if(GainLoss<0) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Red);
|
|
else return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
|
}
|
|
}
|
|
public double GainLossPcnt
|
|
{
|
|
get { return (MarketValue-Exposure)/Exposure; }
|
|
}
|
|
public Brush GainLossPcntColor
|
|
{
|
|
get
|
|
{
|
|
if(!IsActivePosition) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Blue);
|
|
if(GainLoss>0) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Green);
|
|
else if(GainLoss<0) return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Red);
|
|
else return BrushCollection.GetContextBrush(BrushCollection.BrushColor.Black);
|
|
}
|
|
}
|
|
public bool IsActivePosition
|
|
{
|
|
get { return Utility.IsEpoch(SellDate)?true:false; }
|
|
}
|
|
}
|
|
}
|
|
|