using System; using System.ComponentModel; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Linq; using System.Text; using System.Windows.Media; using System.Windows.Input; using System.Windows.Threading; using System.Threading.Tasks; using System.Xml.Serialization; using System.Xml; using System.IO; using MarketData; using MarketData.Numerical; using MarketData.Utils; using MarketData.MarketDataModel; using MarketData.Generator; using MarketData.DataAccess; using TradeBlotter.DataAccess; using TradeBlotter.Command; using TradeBlotter.Model; using TradeBlotter.Cache; using Microsoft.Research.DynamicDataDisplay.DataSources; namespace TradeBlotter.ViewModels { public class OptionsWorksheetViewModel : WorkspaceViewModel { private const int PRICE_FETCH_INTERVAL_MINS = 2; private RelayCommand displayBollingerBandCommand = null; private RelayCommand displayMovingAverageCommand = null; private Option selectedOption = null; private OptionsParams optionsParams = new OptionsParams() { Cashdown = 10000 }; private String companyName; private Price underlierPrice; // this is the price as of the date this record was created. private Price currentPrice; // this is the most recent price in the database private ObservableCollection equityPriceShocksCollection = null; private bool busyIndicator = false; private DispatcherTimer priceTimer = null; private Prices prices = null; private double transactionGL; private bool useRealPrice = false; public OptionsWorksheetViewModel() { priceTimer = new DispatcherTimer(); priceTimer.Tick += new EventHandler(TimerHandler); priceTimer.Interval = new TimeSpan(0, PRICE_FETCH_INTERVAL_MINS, 0); base.DisplayName = "Options Worksheet"; PropertyChanged += OnViewModelPropertyChanged; base.OnPropertyChanged("Title"); } protected override void OnDispose() { if (null == priceTimer) return; priceTimer.Stop(); } private void TimerHandler(Object sender, EventArgs eventArgs) { if (null == underlierPrice) return; Task workerTask = Task.Factory.StartNew(() => { currentPrice = PriceCache.GetInstance().GetLatestPrice(underlierPrice.Symbol); if (null == currentPrice || null==prices) return; if (prices[0].Date.Date.Equals(currentPrice.Date.Date)) { prices[0] = currentPrice; } else prices.Insert(0, currentPrice); }); workerTask.ContinueWith((continuation) => { base.OnPropertyChanged("CurrentPrice"); base.OnPropertyChanged("CurrentPriceDate"); base.OnPropertyChanged("Close"); base.OnPropertyChanged("TransactionGL"); }); } // ******************************************************************************************** P E R S I S T E N C E ******************************************************************************************** public override bool CanPersist() { return true; } public override SaveParameters GetSaveParameters() { SaveParameters saveParams = new SaveParameters(); XmlWriterSettings xmlWriterSettings = new XmlWriterSettings(); xmlWriterSettings.Indent = false; xmlWriterSettings.NewLineHandling = NewLineHandling.None; XmlSerializer optionSerializer = new XmlSerializer(selectedOption.GetType()); StringBuilder optionString=new StringBuilder(); XmlWriter optionWriter = XmlWriter.Create(optionString, xmlWriterSettings); optionSerializer.Serialize(optionWriter, selectedOption); XmlSerializer underlierPriceSerializer = new XmlSerializer(typeof(Price)); StringBuilder underlierPriceString = new StringBuilder(); XmlWriter underlierPriceWriter = XmlWriter.Create(underlierPriceString, xmlWriterSettings); underlierPriceSerializer.Serialize(underlierPriceWriter, underlierPrice); saveParams.Add(new KeyValuePair("Type", "TradeBlotter.ViewModels.OptionsWorksheetViewModel")); saveParams.Add(new KeyValuePair("Option", optionString.ToString())); saveParams.Add(new KeyValuePair("CompanyName", companyName)); saveParams.Add(new KeyValuePair("Price", underlierPriceString.ToString())); return saveParams; } public override void SetSaveParameters(SaveParameters saveParameters) { try { String strSelectedOption = (from KeyValuePair item in saveParameters where item.Key.Equals("Option") select item).FirstOrDefault().Value; String strLatestPrice = (from KeyValuePair item in saveParameters where item.Key.Equals("Price") select item).FirstOrDefault().Value; companyName = (from KeyValuePair item in saveParameters where item.Key.Equals("CompanyName") select item).FirstOrDefault().Value; XmlSerializer optionSerializer = new XmlSerializer(typeof(Option)); StringReader stringReader = new StringReader(strSelectedOption); selectedOption = (Option)optionSerializer.Deserialize(stringReader); XmlSerializer priceSerializer = new XmlSerializer(typeof(Price)); StringReader priceReader = new StringReader(strLatestPrice); underlierPrice = (Price)priceSerializer.Deserialize(priceReader); optionsParams.Symbol = underlierPrice.Symbol; optionsParams.Strike = selectedOption.Strike; optionsParams.Contracts = (int)Math.Round((optionsParams.Cashdown / underlierPrice.Close) / 100.00, 0); optionsParams.Premium = optionsParams.Shares*selectedOption.Bid; optionsParams.ExpirationDate = selectedOption.Expiration; // volatility DateGenerator dateGenerator = new DateGenerator(); int daysToExpiration = dateGenerator.DaysBetweenActual(optionsParams.ExpirationDate, underlierPrice.Date); prices = PricingDA.GetPrices(underlierPrice.Symbol, underlierPrice.Date, daysToExpiration); if(DateTime.Now.Date>=optionsParams.ExpirationDate.Date)currentPrice=PricingDA.GetPrice(underlierPrice.Symbol,optionsParams.ExpirationDate); else currentPrice = PriceCache.GetInstance().GetLatestPrice(underlierPrice.Symbol); optionsParams.VolatilityDays = daysToExpiration; float[] pricesAr = prices.GetPrices(); optionsParams.Volatility = Numerics.Volatility(ref pricesAr); if(DateTime.Now.Date>=optionsParams.ExpirationDate.Date)prices=PricingDA.GetPrices(underlierPrice.Symbol,optionsParams.ExpirationDate,daysToExpiration); else prices = PricingDA.GetPrices(underlierPrice.Symbol,daysToExpiration); EquityPriceShocks equityPriceShocks = MarketData.MarketDataModel.EquityPriceShocks.CreateEquityPriceShocks(underlierPrice, optionsParams); equityPriceShocksCollection = new ObservableCollection(equityPriceShocks); if (null != underlierPrice) base.DisplayName = "Options Worksheet" + "(" + underlierPrice.Symbol + ")"; if (null == optionsParams || null == currentPrice) transactionGL=double.NaN; else { if (optionsParams.Strike < currentPrice.Close)transactionGL=optionsParams.Premium + ((optionsParams.Strike * optionsParams.Shares) - (currentPrice.Close * optionsParams.Shares)); else transactionGL=optionsParams.Premium + ((currentPrice.Close * optionsParams.Shares) - (underlierPrice.Close * optionsParams.Shares)); } base.OnPropertyChanged("Strike"); base.OnPropertyChanged("Bid"); base.OnPropertyChanged("Investment"); base.OnPropertyChanged("ExpirationDate"); base.OnPropertyChanged("Shares"); base.OnPropertyChanged("Contracts"); base.OnPropertyChanged("Premium"); base.OnPropertyChanged("EquityPriceShocks"); base.OnPropertyChanged("UnderlierPrice"); base.OnPropertyChanged("UnderlierPriceDate"); base.OnPropertyChanged("CurrentPrice"); base.OnPropertyChanged("CurrentPriceDate"); base.OnPropertyChanged("OpenInterest"); base.OnPropertyChanged("DisplayName"); base.OnPropertyChanged("Volatility"); base.OnPropertyChanged("ExpirationDateBackground"); base.OnPropertyChanged("Close"); } catch (Exception exception) { MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString()); } } // ****************************************************************************************************************************************************** // *************************************************************** M E N U S U P P O R T ************************************************************** // ****************************************************************************************************************************************************** public ObservableCollection MenuItems { get { ObservableCollection collection = new ObservableCollection(); collection.Add(new TradeBlotter.Model.MenuItem() { Text = "Display Moving Average", MenuItemClickedCommand = DisplayMovingAverage, StaysOpenOnClick = false }); collection.Add(new TradeBlotter.Model.MenuItem() { Text = "Display Bollinger Bands", MenuItemClickedCommand = DisplayBollingerBands, StaysOpenOnClick = false }); return collection; } } public bool UseRealPrice { get { return useRealPrice; } set { useRealPrice = value; base.OnPropertyChanged("UseRealPrice"); } } public ICommand DisplayMovingAverage { get { if (null == displayMovingAverageCommand) { displayMovingAverageCommand = new RelayCommand(param => { SaveParameters saveParams = new SaveParameters(); saveParams.Add(new KeyValuePair("Type", "TradeBlotter.ViewModels.MovingAverageViewModel")); saveParams.Add(new KeyValuePair("SelectedSymbol", underlierPrice.Symbol)); saveParams.Add(new KeyValuePair("SelectedWatchList", Constants.CONST_ALL)); saveParams.Add(new KeyValuePair("SelectedDayCount", "180")); saveParams.Add(new KeyValuePair("IsLegendVisible", "true")); saveParams.Referer=this; WorkspaceInstantiator.Invoke(saveParams); }, param => { return null == underlierPrice ? false : true; }); } return displayMovingAverageCommand; } } public ICommand DisplayBollingerBands { get { if(null==displayBollingerBandCommand) { displayBollingerBandCommand = new RelayCommand(param => { SaveParameters saveParams = new SaveParameters(); saveParams.Add(new KeyValuePair("Type", "TradeBlotter.ViewModels.BollingerBandViewModel")); saveParams.Add(new KeyValuePair("SelectedSymbol", underlierPrice.Symbol)); saveParams.Add(new KeyValuePair("SelectedWatchList", Constants.CONST_ALL)); saveParams.Add(new KeyValuePair("SelectedDayCount", "90")); saveParams.Add(new KeyValuePair("SyncTradeToBand", "false")); saveParams.Add(new KeyValuePair("ShowTradeLabels", "true")); saveParams.Add(new KeyValuePair("IsLegendVisible", "true")); saveParams.Referer=this; WorkspaceInstantiator.Invoke(saveParams); ; }, param => { return null == underlierPrice ? false : true; }); } return displayBollingerBandCommand; } } public bool BusyIndicator { get { return busyIndicator; } set { busyIndicator = value; base.OnPropertyChanged("BusyIndicator"); } } public CompositeDataSource Close { get { if (null == prices) return null; CompositeDataSource compositeDataSource = PricesModel.Close(prices); return compositeDataSource; } } public ObservableCollection EquityPriceShocks { get { return equityPriceShocksCollection; } set { equityPriceShocksCollection = value; base.OnPropertyChanged("EquityPriceShocks"); } } private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs eventArgs) { if (eventArgs.PropertyName.Equals("UseRealPrice")) { if (useRealPrice) priceTimer.Start(); else priceTimer.Stop(); } } public String TransactionGL { get { if (double.NaN.Equals(transactionGL)) return Constants.CONST_DASHES; if (optionsParams.Strike < currentPrice.Close)return Utility.FormatCurrency(transactionGL); return Utility.FormatCurrency(transactionGL); } } public String Volatility { get { if (null == optionsParams) return Constants.CONST_DASHES; return Utility.FormatNumber(optionsParams.Volatility, 2, true); } } public String VolatilityDays { get { if (null == optionsParams) return Constants.CONST_DASHES; return Utility.FormatNumber(optionsParams.VolatilityDays,0, true); } } public String OpenInterest { get { if (null == selectedOption) return Constants.CONST_DASHES; return Utility.FormatNumber(selectedOption.OpenInterest,0,true); } } public String CurrentPrice { get { if (null == currentPrice) return Constants.CONST_DASHES; return Utility.FormatCurrency(currentPrice.Close); } } public String CurrentPriceDate { get { if (null == currentPrice) return Constants.CONST_DASHES; return Utility.DateTimeToStringMMSDDSYYYYHHMMSS(currentPrice.Date); } } public String LossPoint { get { if (null==selectedOption || null == underlierPrice) return Constants.CONST_DASHES; return Utility.FormatCurrency(underlierPrice.Close-selectedOption.Bid); } } public String UnderlierPrice { get { if (null == underlierPrice) return Constants.CONST_DASHES; return Utility.FormatCurrency(underlierPrice.Close); } } public String UnderlierPriceDate { get { if (null == underlierPrice) return Constants.CONST_DASHES; return Utility.DateTimeToStringMMSDDSYYYY(underlierPrice.Date); } } public String Premium { get { if (null == selectedOption) return Constants.CONST_DASHES; return Utility.FormatCurrency(optionsParams.Premium); } } public String Shares { get { if (null == selectedOption) return Constants.CONST_DASHES; return Utility.FormatNumber(optionsParams.Shares,2,true); } } public String Contracts { get { if (null == selectedOption) return Constants.CONST_DASHES; return Utility.FormatNumber(optionsParams.Contracts, 0, true); } } public String ExpirationDate { get { if (null == selectedOption) return Constants.CONST_DASHES; return Utility.DateTimeToStringMMSDDSYYYY(selectedOption.Expiration); } } public Brush TransactionGLBackground { get { if (null == underlierPrice) return null; return underlierPrice.Date < DateTime.Now ? new SolidColorBrush(Colors.Green) : new SolidColorBrush(Colors.Red); } } public Brush ExpirationDateBackground { get { return DateTime.Now.Date>=optionsParams.ExpirationDate.Date?new SolidColorBrush(Colors.Red):new SolidColorBrush(Colors.Green); } } public String Strike { get { if (null == selectedOption) return Constants.CONST_DASHES; return Utility.FormatCurrency(selectedOption.Strike); } } public String Bid { get { if (null == selectedOption) return Constants.CONST_DASHES; return Utility.FormatCurrency(selectedOption.Bid); } } public String Investment { get { if (null == optionsParams) return Constants.CONST_DASHES; return Utility.FormatCurrency(optionsParams.Cashdown); } } public void OnErrorItemHandler(String symbol, String message) { } public override String Title { get { if (null == companyName || null == optionsParams) return "Options Worksheet"; return companyName + " (" + optionsParams.Symbol + ")"; } } } }