Files
TradeBlotter/ViewModels/FloatingWindowViewModel.cs

406 lines
18 KiB
C#

using System;
using System.Diagnostics;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Windows.Threading;
using System.Threading.Tasks;
using System.Threading;
using System.Windows;
using System.Linq;
using System.Text;
using System.Windows.Data;
using MarketData.MarketDataModel;
using MarketData.DataAccess;
using MarketData.Utils;
using MarketData.Integration;
using MarketData;
using TradeBlotter.Command;
using TradeBlotter.ViewModels;
using TradeBlotter.Model;
using TradeBlotter.DataAccess;
using TradeBlotter.Cache;
using TradeBlotter.UIUtils;
using System.Windows.Media;
using Forms=System.Windows.Forms;
using MarketData.Numerical;
using System.Windows.Controls;
using Ticker.Animation;
using Ticker.Views;
using Ticker.Models;
using TradeBlotter.Views;
using System.Windows.Input;
using System.Configuration;
namespace TradeBlotter.ViewModels
{
public class FloatingWindowViewModel : WorkspaceViewModel
{
private static int FLOATING_WINDOW_FEED_INTERVAL_MINUTES=1; // This is the amount of time between feed pulls
private readonly FeedManager manager = null;
private Ticker<TickerItemView> ticker=null;
private Panel mainPanel=null;
private bool isPaused=false;
private int itemCount=0;
private RelayCommand togglePlayPausedCommand;
private DateTime lastEarningsAnnouncementBroadcast;
private DateTime lastAnalystRatingsBroadcast;
private DateTime lastPremarketBroadcast;
public FloatingWindowViewModel(Panel panel)
{
manager=new FeedManager();
manager.FeedIntervalMinutes=FLOATING_WINDOW_FEED_INTERVAL_MINUTES;
mainPanel=panel;
ticker = new Ticker<TickerItemView>(mainPanel);
ticker.ItemDisplayed += ticker_ItemDisplayed;
manager.FeedSourceEventHandler+=GetFeedItemsEventHandler;
manager.NewFeedItem+= (o,e)=>AddItem(e.Item);
base.OnPropertyChanged("IsPaused");
PropertyChanged += OnFloatingViewModelPropertyChanged;
manager.Start();
ticker.Start();
}
public override SaveParameters GetSaveParameters()
{
return null;
}
private void OnFloatingViewModelPropertyChanged(object sender, PropertyChangedEventArgs eventArgs)
{
if (eventArgs.PropertyName.Equals("IsPaused"))
{
if(IsPaused && ticker.IsRunning)
{
ticker.Stop();
}
else if(!IsPaused && !ticker.IsRunning)
{
ticker.Start();
}
}
}
// The feed does not persist contents between successive runs. It uses feed cache to filter previously displayed items for the day
public override void SetSaveParameters(SaveParameters saveParameters)
{
}
public override bool CanPersist()
{
return false;
}
//*****************************************************************************************************************************************************************************
private void AddItem(FeedItem item)
{
Application.Current.Dispatcher.Invoke((Forms.MethodInvoker)delegate()
{
ticker.Items.Enqueue(new TickerItemView(item));
});
}
public String ItemCount
{
get{return itemCount.ToString();}
set{itemCount=int.Parse(value);base.OnPropertyChanged("ItemCount");}
}
public bool IsPaused
{
get{return isPaused;}
set{isPaused=value;base.OnPropertyChanged("IsPaused");}
}
public ICommand TogglePlayPauseCommand
{
get
{
if (togglePlayPausedCommand == null)
{
togglePlayPausedCommand = new RelayCommand(action => {IsPaused=!IsPaused;},action => { return true; });
}
return togglePlayPausedCommand;
}
}
private void ticker_ItemDisplayed(object sender, ItemEventArgs<TickerItemView> e)
{
manager.MarkFeedAsRead(e.Item.FeedItem);
itemCount=manager.GetFeedItemCount();
base.OnPropertyChanged("ItemCount");
}
public List<FeedItem> GetFeedItemsEventHandler()
{
bool itemsAdded=false;
List<FeedItem> feedItems=new List<FeedItem>();
List<String> openSymbols=new List<String>();
List<FeedItem> holidayStatusFeedItems=HolidayStatusToFeedItems();
if(null!=holidayStatusFeedItems){feedItems.AddRange(holidayStatusFeedItems);itemsAdded=holidayStatusFeedItems.Count>0?true:itemsAdded;}
List<FeedItem> networkStatusFeedItems=NetworkStatusToFeedItems();
if(null!=networkStatusFeedItems){feedItems.AddRange(networkStatusFeedItems);itemsAdded=networkStatusFeedItems.Count>0?true:itemsAdded;}
if((Utility.IsEpoch(lastPremarketBroadcast)||DateTime.Now-lastPremarketBroadcast>new TimeSpan(0,5,0))) // earnings broadcast every 5 minutes
{
lastPremarketBroadcast=DateTime.Now;
List<FeedItem> premarketFeedItems=PremarketToFeedItems();
if(null!=premarketFeedItems) { feedItems.AddRange(premarketFeedItems); itemsAdded=premarketFeedItems.Count>0?true:itemsAdded; }
}
List<FeedItem> headlineFeedItems=HeadlinesToFeedItems();
if(null!=headlineFeedItems){feedItems.AddRange(headlineFeedItems);itemsAdded=headlineFeedItems.Count>0?true:itemsAdded;}
try{openSymbols=PortfolioDA.GetOpenSymbols();}catch(Exception){;}
List<FeedItem> openPricesFeedItems=OpenPricesToFeedItems(openSymbols);
if(null!=openPricesFeedItems){feedItems.AddRange(openPricesFeedItems);itemsAdded=openPricesFeedItems.Count>0?true:itemsAdded;}
if(Utility.IsEpoch(lastAnalystRatingsBroadcast)||DateTime.Now-lastAnalystRatingsBroadcast>new TimeSpan(0,20,0)) // analyst ratings broadcast every 20 minutes
{
lastAnalystRatingsBroadcast=DateTime.Now;
List<FeedItem> analystRatingsFeedItems=AnalystRatingsToFeedItems(openSymbols);
if(null!=analystRatingsFeedItems){feedItems.AddRange(analystRatingsFeedItems);itemsAdded=analystRatingsFeedItems.Count>0?true:itemsAdded;}
}
if(Utility.IsEpoch(lastEarningsAnnouncementBroadcast)||DateTime.Now-lastEarningsAnnouncementBroadcast>new TimeSpan(0,20,0)) // earnings broadcast every 20 minutes
{
lastEarningsAnnouncementBroadcast=DateTime.Now;
List<FeedItem> earningsAnnouncementFeedItems=EarningsAnnouncementsToFeedItems(openSymbols);
if(null!=earningsAnnouncementFeedItems){feedItems.AddRange(earningsAnnouncementFeedItems);itemsAdded=earningsAnnouncementFeedItems.Count>0?true:itemsAdded;}
}
return feedItems;
}
// ***************************************************************************************************************************************************
// ************************************************************************ F E E D C O N V E R T E R S ********************************************
// ***************************************************************************************************************************************************
private List<FeedItem> NetworkStatusToFeedItems()
{
List<FeedItem> feedItems=new List<FeedItem>();
try
{
//if(NetworkStatus.IsNetworkAvailable())return null;
if(NetworkStatus.IsInternetConnected())return null;
FeedItem feedItem=new FeedItem();
feedItem.Description="Network is unavailable";
feedItem.Title=String.Format("Network is unavailable");
feedItem.Link="http://192.168.1.1/";
feedItem.Source="Network";
feedItem.PubDate=DateTime.Now.ToShortDateString()+" "+Utility.DateTimeToStringHHMMSS(DateTime.Now);
feedItem.Guid="Network"+feedItem.PubDate;
feedItem.BrushAssigment=FeedBrushes.BrushColor.Red;
feedItems.Add(feedItem);
return feedItems;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
return null;
}
}
private List<FeedItem> HolidayStatusToFeedItems()
{
List<FeedItem> feedItems=new List<FeedItem>();
try
{
DateTime today=DateTime.Now.Date;
DateGenerator dateGenerator = new DateGenerator();
if(!dateGenerator.IsHoliday(today.Date))return feedItems;
String holidayDescription = HolidayDA.GetHolidayDescription(today);
FeedItem feedItem=new FeedItem();
feedItem.Description="Market is closed today - "+holidayDescription+".";
feedItem.Title=String.Format("Market is closed today for "+holidayDescription+".");
feedItem.Link="http://192.168.1.1/";
feedItem.Source="Holiday";
feedItem.PubDate=DateTime.Now.ToShortDateString()+" "+Utility.DateTimeToStringHHMMSS(DateTime.Now);
feedItem.Guid="Holiday"+feedItem.PubDate;
feedItem.BrushAssigment=FeedBrushes.BrushColor.Red;
feedItems.Add(feedItem);
return feedItems;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
return null;
}
}
private List<FeedItem> PremarketToFeedItems()
{
List<FeedItem> feedItems=new List<FeedItem>();
try
{
String tickerPremarketFeedIdleAfter=ConfigurationManager.AppSettings["TickerPremarketFeedIdleAfter"];
if(null==tickerPremarketFeedIdleAfter)tickerPremarketFeedIdleAfter="09:30:00";
TimeSpan cutoff=TimeSpan.Parse(tickerPremarketFeedIdleAfter);
if(DateTime.Now.TimeOfDay>cutoff)return feedItems;
PremarketElements premarketElements=PremarketDA.GetLatestPremarketData();
if(null==premarketElements||0==premarketElements.Count)return feedItems;
foreach(PremarketElement premarketElement in premarketElements)
{
FeedItem feedItem=new FeedItem();
feedItem.Description=premarketElement.Market;
feedItem.Title=String.Format("{0} {1}({2})",premarketElement.Market,Utility.FormatNumber(premarketElement.ChangeValue,2),Utility.FormatPercent(premarketElement.ChangePercent/100.00));
feedItem.Link="https://money.cnn.com/data/premarket//";
feedItem.Source="CNN";
feedItem.PubDate=premarketElement.Timestamp.ToShortDateString()+" "+Utility.DateTimeToStringHHMMSS(premarketElement.Timestamp);
feedItem.Guid=premarketElement.Market+feedItem.PubDate;
if(premarketElement.ChangePercent<0)feedItem.BrushAssigment=FeedBrushes.BrushColor.Red;
else feedItem.BrushAssigment=FeedBrushes.BrushColor.Green;
feedItems.Add(feedItem);
}
return feedItems;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
return null;
}
}
private List<FeedItem> EarningsAnnouncementsToFeedItems(List<String> openSymbols)
{
List<FeedItem> feedItems=new List<FeedItem>();
int maxDaysFromToday=2;
try
{
DateTime today=DateTime.Now;
String tickerEarningsAnnouncementMaxDays=ConfigurationManager.AppSettings["TickerEarningsAnnouncementMaxDays"];
if(null!=tickerEarningsAnnouncementMaxDays)int.TryParse(tickerEarningsAnnouncementMaxDays,out maxDaysFromToday);
List<EarningsAnnouncementModel> earningsAnnouncementModels = new List<EarningsAnnouncementModel>();
List<String> symbols = WatchListDA.GetWatchList("valuations");
foreach (String symbol in symbols)
{
EarningsAnnouncementModel earningsAnnouncementModel=CompositeDA.GetEarningsAnnouncement(symbol);
if(null==earningsAnnouncementModel)continue;
if (earningsAnnouncementModel.DaysFromToday > maxDaysFromToday || earningsAnnouncementModel.DaysFromToday<0) continue;
earningsAnnouncementModels.Add(earningsAnnouncementModel);
}
earningsAnnouncementModels.Sort();
foreach(EarningsAnnouncementModel earningsAnnouncementModel in earningsAnnouncementModels)
{
int days=Math.Abs(earningsAnnouncementModel.DaysFromToday);
FeedItem feedItem=new FeedItem();
feedItem.Description=earningsAnnouncementModel.Symbol;
if(0==days)feedItem.Title=String.Format("Earnings Annoucement for {0} due today",earningsAnnouncementModel.Symbol);
else feedItem.Title=String.Format("Earnings Annoucement for {0} due in {1} {2} ",earningsAnnouncementModel.Symbol,days,days>1?"days":"day");;
feedItem.Link="https://www.zacks.com/stock/research/"+earningsAnnouncementModel.Symbol+"/earnings-announcements";
feedItem.Source="Earnings Announcements";
feedItem.PubDate=today.ToShortDateString()+" "+Utility.DateTimeToStringHHMMSS(DateTime.Now);
feedItem.Guid="Earnings Announcement "+earningsAnnouncementModel.Symbol+" "+today+" "+Utility.DateTimeToStringHHMMSS(DateTime.Now);
if(openSymbols.Any(x=>x.Equals(earningsAnnouncementModel.Symbol)))feedItem.BrushAssigment=FeedBrushes.BrushColor.Red;
feedItems.Add(feedItem);
}
return feedItems;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
return feedItems;
}
}
private List<FeedItem> AnalystRatingsToFeedItems(List<String> openSymbols)
{
List<FeedItem> feedItems=new List<FeedItem>();
List<String> watchListSymbols = WatchListDA.GetWatchList("valuations");
if(null==watchListSymbols)watchListSymbols=new List<String>();
if(null==openSymbols)openSymbols=new List<String>();
try
{
DateTime today=DateTime.Now;
AnalystRatings analystRatings=AnalystRatingsDA.GetAnalystRatings(today);
foreach(AnalystRating analystRating in analystRatings)
{
if(!openSymbols.Any(x=>x.Equals(analystRating.Symbol)) && !watchListSymbols.Any(x=>x.Equals(analystRating.Symbol)))continue;
StringBuilder sb=new StringBuilder();
FeedItem feedItem=new FeedItem();
sb.Append(analystRating.Symbol).Append("(").Append(analystRating.Type).Append(")").Append(":").Append(analystRating.RatingsChange);
if(0.00!=analystRating.PriceTarget)sb.Append(":").Append(Utility.FormatCurrency(analystRating.PriceTarget));
String lineData=sb.ToString();
feedItem.Description=analystRating.Symbol;
feedItem.Title=lineData;
feedItem.Link="https://www.google.com/search?q="+Uri.EscapeDataString(analystRating.Symbol)+"/";
feedItem.Source="Analyst Ratings";
feedItem.PubDate=today.ToShortDateString()+" "+Utility.DateTimeToStringHHMMSS(DateTime.Now);
feedItem.Guid="Analyst Ratings"+analystRating.Symbol+" "+today+" "+Utility.DateTimeToStringHHMMSS(DateTime.Now);;
if(openSymbols.Any(x=>x.Equals(analystRating.Symbol)&&analystRating.Type.Equals("Downgrades")))feedItem.BrushAssigment=FeedBrushes.BrushColor.Red;
else if(openSymbols.Any(x=>x.Equals(analystRating.Symbol)&&analystRating.Type.Equals("Upgrades"))) feedItem.BrushAssigment=FeedBrushes.BrushColor.Green;
feedItems.Add(feedItem);
}
return feedItems;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
return feedItems;
}
}
private List<FeedItem> OpenPricesToFeedItems(List<String> openSymbols)
{
List<FeedItem> feedItems=new List<FeedItem>();
try
{
DateTime pricingDate=DateTime.Now;
DateGenerator dateGenerator=new DateGenerator();
foreach(String symbol in openSymbols)
{
Price todaysPrice=PricingDA.GetPrice(symbol);
if(null==todaysPrice)continue;
DateTime prevDate=dateGenerator.FindPrevBusinessDay(todaysPrice.Date);
Price prevPrice=PricingDA.GetPrice(symbol,prevDate);
if(null==prevPrice)continue;
double change=(todaysPrice.Close-prevPrice.Close)/prevPrice.Close;
String strPercentChange=Utility.FormatPercent(change);
FeedItem feedItem=new FeedItem();
feedItem.Description=symbol;
feedItem.Title=symbol+":"+strPercentChange;
feedItem.Link="https://www.google.com/search?q="+Uri.EscapeDataString(symbol)+"/";
feedItem.Source="Price Feed";
feedItem.PubDate=todaysPrice.Date.ToShortDateString()+" "+Utility.DateTimeToStringHHMMSS(DateTime.Now);
feedItem.Guid=symbol+todaysPrice.Date.ToShortDateString()+":"+strPercentChange;
if(change<0)feedItem.BrushAssigment=FeedBrushes.BrushColor.Red;
else if(change>0)feedItem.BrushAssigment=FeedBrushes.BrushColor.Green;
else feedItem.BrushAssigment=FeedBrushes.BrushColor.Normal;
feedItems.Add(feedItem);
}
return feedItems;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
return feedItems;
}
}
private List<FeedItem> HeadlinesToFeedItems()
{
List<FeedItem> feedItems=new List<FeedItem>();
try
{
DateTime today=DateTime.Now;
MarketData.MarketDataModel.Headlines headlines=HeadlinesDA.GetHeadlines(today);
for(int index=0;index<headlines.Count;index++)
{
MarketData.MarketDataModel.Headline headline=headlines[index];
FeedItem feedItem=new FeedItem();
feedItem.Description=headline.Entry;
feedItem.Title=headline.Symbol+":"+headline.Entry;
feedItem.Link="https://www.google.com/search?q="+Uri.EscapeDataString(headline.Entry)+"/";
feedItem.Source=headline.Source;
feedItem.PubDate=headline.Date.ToShortDateString()+" "+Utility.DateTimeToStringHHMMSS(headline.Modified);
feedItem.Guid=headline.Symbol+headline.Date.ToShortDateString()+":"+Utility.DateTimeToStringHHMMSS(headline.Modified)+":"+headline.Entry;
feedItems.Add(feedItem);
}
return feedItems;
}
catch(Exception exception)
{
MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
return feedItems;
}
}
public virtual void Dispose()
{
if(null!=ticker)ticker.Stop();
if(null!=manager)manager.Dispose();
}
}
}