406 lines
18 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|