using Microsoft.IdentityModel.Tokens; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.Drawing; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Runtime.InteropServices; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using System.Web; using System.Windows.Forms; using System.Xml; using TIWebApi.Clients.MarketData; using TradeIdeas.Logging; using TradeIdeas.MiscSupport; using TradeIdeas.ServerConnection; using TradeIdeas.TIProData; using TradeIdeas.TIProData.Interfaces; using TradeIdeas.XML; using static System.Windows.Forms.VisualStyles.VisualStyleElement.TaskbarClock; using static TradeIdeas.TIProGUI.CustomChannelList; namespace TradeIdeas.TIProGUI { /* The GuiEnvironment class contains a lot of global variables shared by the various * items in this library. This is typically the stuff that would be set by the main * program and then used by most or all of the GUI. */ public static class GuiEnvironment { //Aids in manipulating data for the indices feature in the tool bar (MainForm.cs) public struct IndicesData { public string Symbol; public string Name; public double? Change; public double? Last; public double? PercentChange; public double? MaxChange; public double? MaxPercentChange; public bool Hidden; public int Order; }; public static string INDEX_FONT_FAMILY = "Microsoft Sans Serif"; /// /// Use this to find the GUI thread. /// public static readonly Control ThreadingControl; /// /// Call this to execute code in the GUI thread. If we are already in the GUI thread, /// execute the code immediately and return from this function when it is done. Otherwise, /// send the code to the GUI thread to be executed, and immediately return from this /// function. /// /// To execute. public static void BeginInvokeIfRequired(MethodInvoker code) { if (ApplicationRunning) ThreadingControl.BeginInvokeIfRequired(code); //else // Debug.Write("Ignoring call to the GUI after the GUI has shut down."); // We do see this else case! When shutting down Control.InvokeRequired breaks down. // We we added this. } static GuiEnvironment() { // This assertion will fail because we are called before Application.Run() //Debug.Assert(Application.MessageLoop); ThreadingControl = new Control(); var keepTheCompilerFromComplainingIJustNeedTheSideEffects = ThreadingControl.Handle; ApplicationRunning = true; Application.ApplicationExit += delegate { ApplicationRunning = false; }; } public static bool ApplicationRunning { get; private set; } // For simplicity there is just one global variable set by the main program and used by // the GUI library. This could be empty (but not null) and the GUI library would not crash // However, without the list of translations the GUI would not have much value. It would // show "***" in a lot of places. The nodes in this list are probably not whole xml files, // but each one is probably a subset of the whole file available to the main program. public static List XmlConfig = new List(); public static List AppConfig = new List(); public static bool ReceivedGlobalSettings = false; /// /// Constant string used to indentfy a hidden form that is used by the Inno setup /// script to determine if TI Pro is currently running. /// public const string TIPRO_IS_RUNNING = "TradeIdeasProIsRunning"; // This includes space for the border. By setting these in advance, rather than // waiting for the icon to come in from the server, we can do the layout immediately. public const int ICON_WIDTH = 44; public const int ICON_HEIGHT = 24; public static bool RunningWin10 = false; public static int WIDTH_INCREASE = 14; public static int HEIGHT_INCREASE = 7; public static int SUBSCRIBED_ODDSMAKER_TRIALS = 2147483647; public static string DEFAULT_CONECCTION_HOST = "www.trade-ideas.com"; public static int DEFAULT_CONECCTION_PORT = 8844; /// /// Contains the current weblink settings. Used to save to the user config settings file. /// public static XmlNode WebLinkSettings = null; /// /// The authorizations as populated from the server through the perms_command and perms_is_authorized in MainForm.cs. /// public static Dictionary Authorizations = new Dictionary(); /// /// Gets executed when the robot authorization is received from the server. /// public static Dictionary AuthorizationsCode = new Dictionary(); /// /// Contains a current list of RowData objects representing the list of AI trades. This gets /// updated each time we receive a new AI update from the server. If the user isn't authorized /// then this list will be empty. /// public static List CurrentAiTrades = new List(); /// /// Contains a current list of RowData objects representing any orders in B+. Can be used for /// plotting orders on charts. /// public static List CurrentOrders = new List(); /// /// Contains a current list of RowData objects representing any positions in B+. Can be used for /// plotting position data on charts. /// public static List CurrentPositions = new List(); /// /// Contains a current list of RowData objects representing any executions in B+. Can be used for /// plotting execution data on charts. /// public static List CurrentExecutions = new List(); /// /// The order was moved by the user in the chart. This code will be executions after placement if not null. /// public static Action OrderChartMoved = null; public static Action OnDefaultTradeSizeUpdated = null; /// /// Gets the program version identifier. /// /// public static string GetProgramVersionId() { string assemblyVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); return assemblyVersion; } public static List ChartTradeAccounts { get; set; } = new List(); public static List DefaultHashtags { get; set; } = new List(); public static List TwitterHashtags { get; set; } = new List(); public static int AiSharesPerTrade = 1; public static double AiDollarsPerTrade = 1000; public static double AiRiskDollars = 100; public static AiPositionSizing AiPositionSizingType = AiPositionSizing.FixedShares; public enum AiPositionSizing { FixedShares, FixedDollars, BasedOnStopLoss }; public static Form MAIN_FORM_OBJECT; public static bool CLICKED_MAIN_FORM_MENU = false; //for window positoning public static bool CLICKED_VIEW_STRATEGY_TRADES = false; //for window positoning public static bool CLICKED_VIEW_ALL_TRADES = false; //for window positoning public static bool CLICKED_CHARTS = false; //for window positoning public static bool OPTIMA = false; //for Optima customizations. public static bool SPC = false; //for SPC sponsored build. public static bool ETRADE = false; //for E*TRADE sponsored build. public static bool TRADESTATION = false; //for TradeStation sponsored build. // We retrieve the logo bitmap resource here to avoid an Argument exception when // loading the resource in the Charts typically when you load a layout, close all windows, // and then load the layout again, where the layout is opening 25 chart windows. public static Bitmap LOGO_BW = TradeIdeas.TIProGUI.Properties.Resources.logo_bw; public static Icon Icon { // This exists for historical reasons. Old code could be changed to use DefaultIcon // directly. get { return DefaultIcon.Icon; } // We could create a setter, which also uses DefaultIcon. But it's better to use // DefaultIcon directly in that case. And that probably only happens in the main // program. } public static string statusString = ""; /// /// Send a use case to the server. /// Typically any interesting user interaction, like clicking on a button, will be recorded on the server side. /// /// A description of the action. /// Currently the server limits this string to 50 characters and it should really be only ASCII. /// We need to send data to the server. public static void RecordUseCase(string description, ISendManager sendManager) { if (RecordUseCaseEnabled) sendManager.SendMessage(TalkWithServer.CreateMessage("command", "record_use_case", "description", description)); } public static bool LimitAlertAfterHours = true; public static bool WarnOnPrivateList = true; public static string WarnOnPrivateListMessage = ""; public static string DefaultAlertTemplate = ""; public static string AlertWindowAlertTemplate = ""; public static string PriceAlertAlertTemplate = ""; public static string MultiStrategyWindowAlertTemplate = ""; public static string StrategyTradeWindowAlertTemplate = ""; public static string RBI_GBIWindowAlertTemplate = ""; public static string ExecutionsGridAlertTemplate = ""; public static string TwitterAPIKey = "Td78oRnSA4GavIuinBPHGVQIZ"; public static string TwitterAPISecret = "cFiBgssn8So8tfzNsRM9W5l4lUBklv0nuoCTORBKmHZ85ypDlS"; public static bool TwitterUseExternalComposer = true; public static bool RecordUseCaseEnabled = true; public static bool AlignSymbolPlusIconLeft = true; public static int UserId = 0; /// /// Records the use case for the special case of an externally linked symbol. /// /// RowData object that was clicked on. /// Code for source of user click. Alert = alert window, TopList = top list, MS = Multistrategy, Ticker = compare count ticker /// Optional sendManager. If null this function will attempt to retrieve the default. If not found the use case won't be sent. public static void RecordExternalLinkingUseCase(RowData rowData, string source, ISendManager sendManager = null) { RecordExternalLinkingUseCase(rowData.GetSymbol(), source, sendManager); } public static void RecordExternalLinkingUseCase(string symbol, string source, ISendManager sendManager = null) { if (null == sendManager && _connectionMasterByName.ContainsKey("")) sendManager = _connectionMasterByName[""].SendManager; if (null != sendManager) GuiEnvironment.RecordUseCase(source + ".Grid.ExternalLinking." + symbol, sendManager); } /// /// Used for one-off troubleshooting. Must have DEBUG_LOGGING enabled in the XML file. /// /// Message to be logged. public static void LogMessage(string message) { try { if (GuiEnvironment.XmlConfig.Node("DEBUG_LOGGING").Property("ENABLED", "NO") == "YES") { string logFile = GetLogFilePath(); using (FileStream fileStream = new FileStream(logFile, FileMode.Append, FileAccess.Write, FileShare.None)) { using (StreamWriter streamWriter = new StreamWriter(fileStream)) { streamWriter.WriteLine(GetCurrentTimestamp() + "\t" + message); } } } } catch { } } public static string GetCurrentTimestamp() { DateTime now = DateTime.Now; return now.ToString("MM/dd/yyyy hh:mm:ss.fff tt"); } private static string GetLogFilePath() { string logFile = DataDirectory + "\\debuglog." + DateTime.Now.ToString("yyyyMMdd") + ".txt"; return logFile; } //When saving and opening files, the state of the column visibility is retained //via code within the DataGridViewSupport,along with the SaveLayout and RestoreLayout //of the respective TopList,Alert,Multistrategy Forms. However when one changes the //language on the Main Form, we're neither saving nor restoring any file. Thus, a bug occurs //whereby any hidden columns will re-appear upon language-change. //The below bool was created and is checked within DataGridViewSupport to ensure //retention of any hidden columns upon language-change... public static bool LanguageChanged = false; //This bool is associated with the Options window. //It is used to determine whether gridlines of alert,toplist //and multistrategy windows will be visible or not... public static bool ShowGridLines = true; // This determines whether SnapToGrid is in effect or not. public static bool SnapToGrid = true; // This determines whether the CSVLogger tool is displayed in alert windows. public static bool CSVLogger = false; // This determines whether small config window is used or not. public static bool SmallConfigWindow = true; //This bool is associated with Options window. //Namely allowing the user to highlight whole grid rows instead // of indivdual cell within an alert,toplist, or multistrategy window public static bool HighlightGridRow = true; //The bool below is for context sensitive help within the Options window... public static bool ShowHelp = true; //This determines if we mute the alert sounds public static bool MuteSounds = false; //This determines if we sow the dev tools for the chrome browser public static bool ShowDevTools = false; //This is the list of recent symbol lists displayed in the send to symbol list menu public static List RecentSymbolLists = new List(); //This is the list of excluded news sources. public static List ExcludedNewsSources = new List(); //The below bool if for Integration Support (Options window...linking) //single click is "true", double click is "false" . Combo box selections... public static bool IntegrationSupportSingleClick = false; public static bool IntegrationSupportUpDownKeys = true; public static bool SendToExternalLinking = true; //The following is used in Brokerage Plus. public const int MARKET_HOURS_SECONDS = 23400; /// /// Gets the market status string. Now holiday aware. /// public static string GetMarketStatus() { DateTime marketNow = GetNowInMarketTimeZone(); DateTime marketOpen = GetMarketOpenLocalTime(marketNow); DateTime marketClose = GetMarketCloseLocalTime(); DateTime now = ServerFormats.Now; if (now < marketOpen) return "Market opens " + DataCell.GetRelativeTime(marketOpen); else if (now < marketClose) return "Market closes " + DataCell.GetRelativeTime(marketClose); else return "Market closed " + DataCell.GetRelativeTime(marketClose); } /// /// Returns true if today is a market day else false. This is holiday and weekend aware. /// /// public static bool IsMarketOpenToday() { DateTime marketNow = GetNowInMarketTimeZone(); DateTime marketOpen = GetMarketOpenLocalTime(marketNow); if (marketNow.Date == marketOpen.Date) return true; else return false; } /// /// Returns true if the market is open now else false. This is holiday and weekend aware. /// /// public static bool IsMarketOpenNow() { DateTime marketNow = GetNowInMarketTimeZone(); DateTime marketOpen = GetMarketOpenLocalTime(marketNow); DateTime marketClose = GetMarketCloseLocalTime(); DateTime now = ServerFormats.Now; if (now >= marketOpen && now <= marketClose) return true; else return false; } //The below item for the Font changes...The defaults //are with respect to the Options Window public static float DEFAULT_FONTSIZE = 8.25F; public static string DEFAULT_FONT = "Microsoft Sans Serif"; public static Font FontSettings = new Font(DEFAULT_FONT, 12, FontStyle.Regular, GraphicsUnit.Point); //The below bools pertain to the user wishing to connect his StockTwits //account to his twitter, facebook, or LinkedIn accounts (Options Window) public static bool stTwitter = false; public static bool stFacebook = false; public static bool stLinkedIn = false; public static String MarketingLink = "https://www.trade-ideas.com/login/permission.html"; public static String BrokeragePlusMarketingLink = "https://www.trade-ideas.com/products/automate-trade-ideas-brokerage-plus/"; public static String UserMarketingData = ""; public static String MarketExploreLink = "https://www.trade-ideas.com/guide/chapter/25/25Market_Explorer.html"; public static String ChannelDescriptionsLink = "https://www.trade-ideas.com/education/channel-bar-descriptions/"; // The following strings and bool are used to support the default layout channel. public static String DefaultLayoutCode = ""; public static String DefaultLayoutFile = ""; public static bool DefaultLayoutOn = false; /// /// Stores chart annotations per symbol and timeframe. Persists between restarts. /// public static ChartAnnotationCache ChartAnnotationCache = new ChartAnnotationCache(); /// /// Stores browser chart drawing tools per symbol and timeframe. Persists between restarts. /// public static BrowserChartDrawingToolsCache BrowserChartDrawingToolsCache = new BrowserChartDrawingToolsCache(); //this bool ensures that the login webpage only displays once, and that //is when the user sucessfully logs into his stocktwits account. This prevents //the login screen from flashing briefly each time the user sends a message. public static bool isStocktwitsInintialized = false; public static bool isFacebookInintialized = false; //odds maker results settings for the user. these are stored in ConfigSettings and user.config public static int StartingEquity = 50000; public static double Commission = 0.0; public static double Slippage = 0.0; public static int ShareSize = 100; public static PositionSizingType PositionSizing = PositionSizingType.Shares; // Used for Optima public static double CommissionMinimum = 2.50; public static double SlippageMinimum = 0.01; // robot settings for use in makeSendToContextMenu /// /// Code that gets executed when a context window is opening in a Trade-Ideas tool. /// public static Action RobotStrategyMenuCode = null; /// /// Add and clear the edit chart strategy context menu to the charts. /// public static Action RobotStrategyEditMenu = null; /// /// Code that gets executed when a context window is opening in a Trade-Ideas Chart for selecting the default strategy. /// public static Action ChartStrategyMenuCode = null; /// /// Called when the user has initiated a trade by selecting a price on the chart. /// public static Action ChartTradeInitiated = null; public static Action ChartManualTradeInitiated = null; /// /// Called when the user select the open strategies link. /// public static Action ChartOpenStrategyTab = null; /// /// Called when the user select the open B+. /// public static Action ChartOpenRobotInstance = null; /// /// Called when the user select the edit default strategy link. /// public static Action ChartEditDefaultStrategy = null; /// /// Object containing all of the requirements to allow trading actions. /// public static TradeRequirementsSettings TradeRequirements = new TradeRequirementsSettings(); /// /// Called when the Chart default requirements are updated. /// public static Action ChartTradeRequirementsUpdated = null; // default odds maker column config type public static OddsMakerColumnConfiguration OddsMakerColumnConfigType = OddsMakerColumnConfiguration.Strategy; public static string DataDirectory = ""; public static string AppNameInternal = ""; //favorite oddsmaker result columns public static List OddsMakerFavoriteFilters = new List(); //common symbol lists public static HashSet CommonSymbolListCodes = new HashSet(); public static readonly WindowIconCache DefaultIcon = new WindowIconCache(); public static CultureManager CultureManager = new CultureManager(); public static List RowDataExtensions = new List(); /// /// RowData handlers for candle data when a user right clicks on a particular candle in a chart. /// public static List CandleDataHandlers = new List(); // recent color list public static List RecentColors = new List(); // This is the callback for external linking. MainForm sets this up on startup so that LayoutManager can // hook up the onSymbolClicked event handler //public static ExternalLinkingForm externalLinkingForm; public static ExternalLinking externalLinking; public static Action ExternalLinkingCode = null; public static Action FormLayoutRestoredCode = null; public static Action
FormLayoutClosedCode = null; /// /// Needed for AutoOptimizer code only at this point. Gets called when MultiStrategy window /// has completely finished restoring. /// public static Action MultiStrategyRestoredCode = null; public static Action WelcomeScreenOpened = null; public delegate void BeforeCloudLinkAction(string link, ref bool actuallyOpen); public static BeforeCloudLinkAction BeforeCloudLinkOpened = null; public static bool KeepRestoredFormsHidden = false; public static bool InHostedEnvironment = false; public static Action CustomSendToSingleStockCode = null; public static Action CustomSendToChartCode = null; public static bool AllowStrategyMenus = true; public static Action BeforeConfiguringCode = null; public static Action AfterConfiguringCode = null; // Determines if windows are positioned relative to the main window public static bool openRelativeToMainWindow = false; public static int currentMainWindowTop = 0; public static int currentMainWindowLeft = 0; public static int newMainWindowTop = 0; public static int newMainWindowLeft = 0; public static Screen CurrentScreen = null; // Determine if opening new windows are docked - RVH20210604 public static bool openInDockWindow = false; // Determine if we need to show instructions on docked windows - RVH20211007 public static bool showDockedWindowInstructions = true; // Determine if data grid columns are auto-sized - RVH20210720 public static bool autosizeColumns = true; // Determine if data grid columns should ignore minimum column sizes set in global settings - RVH20220215 public static bool ignoreMinColumnSizes = true; // Brokerage+ Connect settings. public static bool AutoReconnectBrokers = true; public static Action ShowOptionsForm = null; // Brokerage+ Orders settings. public static bool OEPShowOrderConfirmationPopup = false; public static decimal OEPDefaultOrderQuantity; public static string OEPDefaultOrderQuantityUnits; public static bool OEPOrderMatchPositions = false; public static decimal OEPOrderIncrementDollars; public static int OEPOrderIncrementShares; public static string OEPDefaultOrderSide; public static string OEPDefaultOrderType; public static string OEPDefaultOrderPrice; public static decimal OEPDefaultLimitPriceOffset; public static string OEPDefaultTIFSetting; public static int OEPDefaultTIFCustomValue; public static string OEPDefaultTIFCustomIncrement; public static bool OEPTIFFillAfterHours = false; public static int OEPDefaultTimeStopMinutes; public static string OEPDefaultTimeStopSetting; public static int OEPDefaultTimeStopDays; public static bool OEPStopLoss = false; public static decimal OEPDefaultStopLossValue; public static string OEPDefaultStopLossType; public static bool OEPProfitTarget = false; public static decimal OEPDefaultProfitTargetValue; public static string OEPDefaultProfitTargetType; //public static string OEPOrderMaximumSetting; public static decimal OEPOrderMaximumDollars; public static int OEPOrderMaximumShares; // Brokerage+ Quick Buttons settings. public static List BPlusDefaultButtons = new List(); public static List BPlusActiveButtons = new List(); public static List BPlusCustomButtons = new List(); public static bool DisableBrowserGPUAcceleration = false; public static bool DisableBrowserGPURenderAndFeatureOverrides = false; public static bool CefSharpVerboseLoggingEnabled = false; public static bool DevelopmentMode = false; public static LimitAlerts.LimitAlertsManager LimitAlertsMessageManager; public static List LimitAlerts = new List(); public static bool LoadingLimitAlerts = false; public const int MAXLIMTALERTS = 500; public const int STDLIMTALERTS = 500; // Now standard users get 500 price alerts. public static int MaxLimitAlerts = MAXLIMTALERTS; public static TradeIdeas.TIProGUI.LimitAlerts.LimitAlert oldestLimitAlertForDeletion = new TradeIdeas.TIProGUI.LimitAlerts.LimitAlert(); public const int MAXCHARTS = 20; public const int STDCHARTS = 10; public static int MaxLimitCharts = STDCHARTS; public const int MSEC_BETWEENSURFS = 9000; public static int timeBetweenSurfs = MSEC_BETWEENSURFS; public const int SURFING_ITEMS_PER_WINDOW = 3; public static int surfingItemsPerWindow = SURFING_ITEMS_PER_WINDOW; public static SymbolListForm symbolListForm; /// /// Updates the oldest limit alert which will be deleted if the user is trying to add a new limit alert once the maximum /// number of limit alerts is reached. /// The oldest limit alert for deletion will be selected with the oldest created date starting with alerts with a status of Invalidated, /// Expired, Triggered, and Working in that order. /// public static void UpdateOldestLimitAlertForDeletion() { // Create new limit alert objects these will have an empty Id string and now as Created time/date. TradeIdeas.TIProGUI.LimitAlerts.LimitAlert oldestLimitAlertInvalidated = new TradeIdeas.TIProGUI.LimitAlerts.LimitAlert(); TradeIdeas.TIProGUI.LimitAlerts.LimitAlert oldestLimitAlertExpired = new TradeIdeas.TIProGUI.LimitAlerts.LimitAlert(); TradeIdeas.TIProGUI.LimitAlerts.LimitAlert oldestLimitAlertTriggered = new TradeIdeas.TIProGUI.LimitAlerts.LimitAlert(); TradeIdeas.TIProGUI.LimitAlerts.LimitAlert oldestLimitAlertWorking = new TradeIdeas.TIProGUI.LimitAlerts.LimitAlert(); foreach (TradeIdeas.TIProGUI.LimitAlerts.LimitAlert limitAlert in LimitAlerts) { switch (limitAlert.Status) { case "INVALIDATED": { oldestLimitAlertInvalidated = FindOldestLimitAlert(oldestLimitAlertInvalidated, limitAlert); break; } case "EXPIRED": { oldestLimitAlertExpired = FindOldestLimitAlert(oldestLimitAlertExpired, limitAlert); break; } case "TRIGGERED": { oldestLimitAlertTriggered = FindOldestLimitAlert(oldestLimitAlertTriggered, limitAlert); break; } case "WORKING": { oldestLimitAlertWorking = FindOldestLimitAlert(oldestLimitAlertWorking, limitAlert); break; } default: break; } } // Identify the oldest limit alert with the following status order: INVALIDATED, EXPIRED, TRIGGERED, WORKING. TradeIdeas.TIProGUI.LimitAlerts.LimitAlert oldestLimitAlert = new TradeIdeas.TIProGUI.LimitAlerts.LimitAlert(); if (!String.IsNullOrEmpty(oldestLimitAlertInvalidated.Id)) oldestLimitAlert = oldestLimitAlertInvalidated; else if (!String.IsNullOrEmpty(oldestLimitAlertExpired.Id)) oldestLimitAlert = oldestLimitAlertExpired; else if (!String.IsNullOrEmpty(oldestLimitAlertTriggered.Id)) oldestLimitAlert = oldestLimitAlertTriggered; else if (!String.IsNullOrEmpty(oldestLimitAlertWorking.Id)) oldestLimitAlert = oldestLimitAlertWorking; // Update oldestLimitAlertForDeletion if (!String.IsNullOrEmpty(oldestLimitAlert.Id)) oldestLimitAlertForDeletion = FindOldestLimitAlert(oldestLimitAlertForDeletion, oldestLimitAlert); } /// /// Compare two limit alerts and return the one whose has the oldest created date time. /// /// /// /// private static TradeIdeas.TIProGUI.LimitAlerts.LimitAlert FindOldestLimitAlert(TradeIdeas.TIProGUI.LimitAlerts.LimitAlert oldestLimitAlert, TradeIdeas.TIProGUI.LimitAlerts.LimitAlert limitAlert) { // If oldest limit alert is null then return limit alert if (String.IsNullOrEmpty(oldestLimitAlert.Id)) return limitAlert; // if oldest is later that limitAlert return limitAlert if (oldestLimitAlert.Created.CompareTo(limitAlert.Created) == 1) return limitAlert; return oldestLimitAlert; } /// /// Determines whether config window opens up in the most recent location. Default is true. When false, StartPosition = CenterParent. /// public static bool ConfigWindowRemembersLocation = true; /// /// A list of codes that can generate events from the welcome screen. These codes can be registered at run time and /// are used in the welcome screen url scheme and handled via the CustomCommandTriggered event. /// public static List CustomWelcomeCommands = new List(); /// /// The standard way of sending a symbol to the external linking connector. /// /// The symbol. /// The exchange. /// Name of the list. /// The form. /// The row data. /// Set to true if coming from SurfManager. If false, this turns off surfing if it is running. /// The source window. /// Open the external linking form if there are no links defined or no active links. /// The link channel. /// The Dock Window ID public static void sendSymbolToExternalConnector(string symbol, string exchange, string ListName, Form form = null, RowData rowData = null, bool fromSurfManager = false, string sourceWindow = "", bool promptUserIfEmpty = false, string linkChannel = null, string dockWindowID = null) { // if linkChannel not specified, set to default if (string.IsNullOrEmpty(linkChannel)) linkChannel = SymbolLinkChannels.DefaultLinkChannel; System.Diagnostics.Debug.Print("sendSymbolToExternalConnector(\"" + symbol + "\", \"" + exchange + "\", ...)"); if (null != ExternalLinkingCode) ExternalLinkingCode(form, symbol, exchange); else { // Change external linking position to open near the calling window when promptUserIfEmpty is true. if (promptUserIfEmpty) GuiEnvironment.SetWindowOpeningPosition(externalLinking, form); externalLinking.AddToSymbols(symbol, exchange, rowData, linkChannel, dockWindowID); externalLinking.SendSymbols(promptUserIfEmpty, fromSurfManager, sourceWindow); if (!fromSurfManager) { Surfer.SurfManager surfManager = Surfer.SurfManager.Instance(); if (surfManager.IsRunning || surfManager.Paused) surfManager.Stop(); } } } /// /// Returns the ID (handle) of the MainDockForm containing the passed form. /// /// Docked Child form /// public static string GetDockWindowID(Form form) { string dockWindowID = ""; if (form != null && form.Parent != null) { // Check to see if the parent form is a dock window. WindowDocumentDock dock = form.Parent as WindowDocumentDock; if (dock != null) { dockWindowID = dock.ParentForm.Handle.ToString(); } } return dockWindowID; } /// /// Returns the Link Channel of the MainDockForm containing the passed form. /// /// Docked Child form /// public static string GetDockWindowLinkChannel(Form form) { string dockWindowLinkChannel = ""; if (form.Parent != null) { // Check to see if the parent form is a dock window. WindowDocumentDock dock = form.Parent as WindowDocumentDock; if (dock != null) { MainDockForm mainDockForm = dock.ParentForm as MainDockForm; if (mainDockForm != null) { dockWindowLinkChannel = mainDockForm.LinkChannel; } } } return dockWindowLinkChannel; } /// /// Returns true if the MainDockForm containing the passed form has channel bar menu items added. /// This indicates that a docked channel bar has been loaded into the MainDockForm. /// /// Docked Child form /// public static bool GetDockWindowChannelBarMenuItemsAdded(Form form) { bool dockWindowChannelBarMenuItemsAdded = false; if (form.Parent != null) { // Check to see if the parent form is a dock window. WindowDocumentDock dock = form.Parent as WindowDocumentDock; if (dock != null) { MainDockForm mainDockForm = dock.ParentForm as MainDockForm; if (mainDockForm != null) dockWindowChannelBarMenuItemsAdded = mainDockForm.ChannelBarMenuItemsAdded; } } return dockWindowChannelBarMenuItemsAdded; } /// /// Callback for updating the surfing status. This is so that we can communicate with the /// main TIPro form from TIProGUI. /// public static Action UpdateSurfingStatus = null; /// /// The last time we retrieved the indicator library. /// public static DateTime? LastRetrievedIndicatorLibrary = null; /// /// Stores the current master library of chart indicators. This variable is managed from Charts.cs. /// This list includes parent and child indicators. When a user selects one from this list, a copy of /// it is made (via DeepCopy()) and stored locally in Charts.cs. The indicators in this list are /// never modified directly. /// public static List TiqColumns = new List(); //This popup occurs when user requests a feature that he's not entitled to because he/she is in the "Delayed Data Mode" public static void showPurchasePopup(bool isDemo) { if (isDemo) { PleasePurchase form = new PleasePurchase(); Screen screen = Screen.FromControl(form); Rectangle workingArea = screen.WorkingArea; form.Location = new Point() //https://stackoverflow.com/questions/4601827/how-do-i-center-a-window-onscreen-in-c { X = Math.Max(workingArea.X, workingArea.X + (workingArea.Width - form.Width) / 2), Y = Math.Max(workingArea.Y, workingArea.Y + (workingArea.Height - form.Height) / 2) }; form.ShowDialog(); } } public static void HideMenuItems(ToolStripItemCollection toolStripItems) { try { /* * There is a chance that when you hide a menu item you could end up with two consecutive separators. This * now hides any separators where the previous unhidden item in the collection is also a separator. */ bool lastShownIsSeparator = false; foreach (ToolStripItem toolStripItem in toolStripItems) { ToolStripMenuItem toolStripMenuItem = toolStripItem as ToolStripMenuItem; ToolStripSeparator toolStripSeparator = toolStripItem as ToolStripSeparator; if (null != toolStripMenuItem) { HideMenuItem(toolStripMenuItem); if (toolStripMenuItem.Available) lastShownIsSeparator = false; } else if (null != toolStripSeparator) { if (lastShownIsSeparator) toolStripSeparator.Visible = false; lastShownIsSeparator = true; } } } catch (Exception e) { string debugView = e.Message + e.StackTrace; } } private static void HideMenuItem(ToolStripMenuItem menuItem) { if (menuItem.Text == "---") { menuItem.Visible = false; menuItem.Available = false; } if (menuItem.DropDownItems.Count > 0) HideMenuItems(menuItem.DropDownItems); } public static void HideCheckBox(CheckBox checkbox) { if (checkbox.Text == "---") { checkbox.Visible = false; } } /// /// Stores extra menu items for use in the right click menu items on the forms. /// private static List _extraToolStripMenuItems = new List(); /// /// Add a menu item to be placed on the right click menu in each form. /// /// public static void AddExtraMenuItem(TIToolStripMenuItem toolStripMenuItem) { _extraToolStripMenuItems.Add(toolStripMenuItem); } /// /// Gets a list of the current extra menu items to be displayed in the right click menu items on the forms. /// Each form is responsible for checking this list and adding the extra items where appropriate. /// /// List of extra menu items public static List GetExtraMenuItems() { return _extraToolStripMenuItems; } /// /// Calls tinyurl.com to convert a url into a smaller url. /// Resultant URL will use https:. /// /// The original url. /// null on failure, the smaller url on success. public static string makeTinyURL(string longurl) { try { String address = HttpUtility.UrlEncode(longurl); String tinyURL = new WebClient().DownloadString("https://tinyurl.com/api-create.php?url=" + address); // Modify tinyURL to use https:. tinyURL = tinyURL.Replace("http:", "https:"); return tinyURL; } catch { // When I change the url from tinyurl.com to tinyurlx.com, I get an exception. It's a // DNS error. return null; } } public static void gotoHelpForFilter(string internalCode) { //string urlPrefix = GuiEnvironment.XmlConfig.Node("HELP_PREFIX").PropertyForCulture("BASE", "***"); //string url = urlPrefix + "WSF_Min" + internalCode; string url = GuiEnvironment.GlobalSettings.Node("HELP_PREFIX_FILTER").Property("VALUE", "https://www.trade-ideas.com/help/filter/"); url += $"{internalCode}/"; Process.Start(url); } public static void gotoHelpForAlert(string internalCode) { //string urlPrefix = GuiEnvironment.XmlConfig.Node("HELP_PREFIX").PropertyForCulture("BASE", "***"); //string url = urlPrefix + internalCode; string url = GuiEnvironment.GlobalSettings.Node("HELP_PREFIX_ALERT").Property("VALUE", "https://www.trade-ideas.com/help/alert/"); url += $"{internalCode}/"; Process.Start(url); } public static string AlertDateTimeFormat = ""; public static string AlertDateTimeShortFormat = ""; public static string AlertTimeShortFormat = ""; public static string AlertTimeShorterFormat = ""; public static string AlertTimeFormat = ""; public static string AlertTimeTopListFormat = ""; public static string DateTimePickerFormat = ""; // This determines whether ShowMilitaryTime is in effect or not. public static bool ShowMilitaryTime = false; /// /// When true, performs actions to display military time formats. /// When false, resets time display to be normal. /// /// public static void ApplyShowMilitaryTime(bool showMilitaryTime) { // Reset ShowMilitaryTime GuiEnvironment.ShowMilitaryTime = showMilitaryTime; string currentLanguage = GuiEnvironment.CultureManager.Current.TwoLetterISOLanguageName; if (showMilitaryTime) { AlertDateTimeFormat = GuiEnvironment.AppConfig.Node("GUI_LIB").Node("MILITARY_DATETIME_FORMAT").PropertyForCulture("TEXT", "HH:mm:ss MM/dd/yyyy"); //AlertDateTimeShortFormat = GuiEnvironment.AppConfig.Node("GUI_LIB").Node("MILITARY_DATETIME_SHORT_FORMAT").PropertyForCulture("TEXT", "HH:mm MM/dd/yyyy"); AlertDateTimeShortFormat = GuiEnvironment.AppConfig.Node("GUI_LIB").Node("MILITARY_TIME_SHORT_FORMAT").PropertyForCulture("TEXT", "HH:mm") + " " + CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern; AlertTimeFormat = GuiEnvironment.AppConfig.Node("GUI_LIB").Node("MILITARY_TIME_FORMAT").PropertyForCulture("TEXT", "HH:mm:ss"); // Empty string will use local time format AlertTimeShortFormat = GuiEnvironment.AppConfig.Node("GUI_LIB").Node("MILITARY_TIME_SHORT_FORMAT").PropertyForCulture("TEXT", "HH:mm"); AlertTimeShorterFormat = AlertTimeShortFormat; AlertTimeTopListFormat = GuiEnvironment.AppConfig.Node("GUI_LIB").Node("MILITARY_TIME_FORMAT").PropertyForCulture("TEXT", "HH:mm:ss"); DateTimePickerFormat = GuiEnvironment.AppConfig.Node("GUI_LIB").Node("MILITARY_TIME_SHORT_FORMAT").PropertyForCulture("TEXT", "HH:mm") + " " + CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern; } else { AlertDateTimeFormat = GuiEnvironment.AppConfig.Node("GUI_LIB").Node("DATETIME_FORMAT").PropertyForCulture("TEXT", "h:mm:ss tt MM/dd/yyyy"); //AlertDateTimeShortFormat = GuiEnvironment.AppConfig.Node("GUI_LIB").Node("DATETIME_SHORT_FORMAT").PropertyForCulture("TEXT", "h:mm MM/dd/yyyy"); AlertDateTimeShortFormat = CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern + " " + CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern; AlertTimeFormat = GuiEnvironment.AppConfig.Node("GUI_LIB").Node("TIME_FORMAT").PropertyForCulture("TEXT", ""); // Empty string will use local time format AlertTimeShortFormat = GuiEnvironment.AppConfig.Node("GUI_LIB").Node("TIME_SHORT_FORMAT").PropertyForCulture("TEXT", "h:mm tt"); AlertTimeShorterFormat = GuiEnvironment.AppConfig.Node("GUI_LIB").Node("TIME_SHORTER_FORMAT").PropertyForCulture("TEXT", "h:mm"); AlertTimeTopListFormat = GuiEnvironment.AppConfig.Node("GUI_LIB").Node("TIME_LONG_FORMAT").PropertyForCulture("TEXT", "h:mm:ss"); if (!currentLanguage.Equals("en")) DateTimePickerFormat = GuiEnvironment.AppConfig.Node("GUI_LIB").Node("TIME_PERIOD_FORMAT").PropertyForCulture("TEXT", "h:mm tt M/dd/yyyy"); else DateTimePickerFormat = CultureInfo.CurrentCulture.DateTimeFormat.ShortTimePattern + " " + CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern; } } /// /// Returns the Window Name parameter from the config string. /// /// public static string GetWindowName(string config) { NameValueCollection parameters = HttpUtility.ParseQueryString(config); return parameters["WN"]; } private const string MARKET_TIME_ZONE = "Eastern Standard Time"; public static TimeZoneInfo GetMarketTimeZone() { TimeZoneInfo marketTimeZone = TimeZoneInfo.FindSystemTimeZoneById(MARKET_TIME_ZONE); return marketTimeZone; } /// /// Converts a date value from the source time zone to a value in the destination time zone, /// checking that the kind attribute of the date value matches the source time zone, if not, /// assigns "Unspecified" to this worth. /// /// /// /// /// public static DateTime ConvertTimeCheckingKindProperty(DateTime sourceDate, TimeZoneInfo sourceTimeZone, TimeZoneInfo targetTimeZone) { if (sourceDate.Kind != DateTimeKind.Unspecified) { var needToChange = false; if (sourceDate.Kind == DateTimeKind.Local) { var localZone = TimeZoneInfo.Local; if (sourceTimeZone.Id != localZone.Id) needToChange = true; } else if (sourceDate.Kind == DateTimeKind.Utc) { var utcZone = TimeZoneInfo.Utc; if (sourceTimeZone.Id != utcZone.Id) needToChange = true; } if (needToChange) sourceDate = DateTime.SpecifyKind(sourceDate, DateTimeKind.Unspecified); } // Convert from user selected time zone (ServerFormats.CurrentTimeZone) to the market time zone (Eastern) return TimeZoneInfo.ConvertTime(sourceDate, sourceTimeZone, targetTimeZone); } public static DateTime ToMarketTimeZone(this DateTime value) { // Convert from user selected time zone (ServerFormats.CurrentTimeZone) to the market time zone (Eastern) return GuiEnvironment.ConvertTimeCheckingKindProperty(value, ServerFormats.CurrentTimeZone, GetMarketTimeZone()); } public static DateTimeOffset ToMarketTimeZone(this DateTimeOffset value) { // Convert from user selected time zone (ServerFormats.CurrentTimeZone) to the market time zone (Eastern) return TimeZoneInfo.ConvertTime(value, GetMarketTimeZone()); } public static DateTimeOffset ToLocalTimeZone(this DateTimeOffset value) { // Convert from user selected time zone (ServerFormats.CurrentTimeZone) to the market time zone (Eastern) return TimeZoneInfo.ConvertTime(value, ServerFormats.CurrentTimeZone); } public static bool IsPreOrPostMarket(this DateTime value) { bool isPreMarket = value.IsPreMarket(); bool isPostMarket = value.IsPostMarket(); return isPreMarket || isPostMarket; } public static bool IsPreMarket(this DateTime value) { DateTime marketOpen = GetMarketOpenInMarketTimeZone(value); DateTime preMarketOpen = marketOpen.AddMinutes(MarketHoursSettingsModel.PRE_MARKET_MINUTES * -1); DateTime currentValue = value.ToMarketTimeZone().ToUniversalTime(); return currentValue >= preMarketOpen.ToUniversalTime() && currentValue < marketOpen.ToUniversalTime(); } public static bool IsPostMarket(this DateTime value) { DateTime marketClose = GetMarketCloseInMarketTimeZone(value); DateTime currentValue = value.ToMarketTimeZone().ToUniversalTime(); DateTime postMarketClose = marketClose.AddMinutes(MarketHoursSettingsModel.POST_MARKET_MINUTES); return currentValue >= marketClose.ToUniversalTime() && currentValue < postMarketClose.ToUniversalTime(); } /// /// Extension Method. Determines if a specified DateTime is prior to the Premarket time (4AM) and later than 12AM same day /// /// The time in the market timezone /// public static bool IsPriorToPremarket(this DateTime value) { var defaultPreMarketMinutesBeforeOpen = 330; // Default to 4:00AM ET var premarketMinutes = GuiEnvironment.GlobalSettings.Node("CHARTS").Node("PREMARKET_MINUTES").Property("VALUE", defaultPreMarketMinutesBeforeOpen); DateTime marketOpen = GetMarketOpenInMarketTimeZone(value); DateTime preMarketStart = marketOpen.AddMinutes(-premarketMinutes); return value >= preMarketStart.Date && value < preMarketStart; } /// /// Extension method. Based on the day of the week (Weekends) and the HolidayManager, determines if for a specific day the market was closed /// /// The time in the market timezone /// public static bool IsMarketClosed(this DateTime value) { return HolidayManager.Instance().IsMarketClosed(value) || IsWeekend(value); } /// /// Extension Method. Returns true if the Day of the week of the specific date is Saturday or Sunday /// /// The time in the market timezone /// public static bool IsWeekend(this DateTime value) { return value.DayOfWeek == DayOfWeek.Sunday || value.DayOfWeek == DayOfWeek.Saturday; } public static bool IsPreMarketWithTimeCheck(this DateTime value) { var premarketMinutes = MarketHoursSettingsModel.PRE_MARKET_MINUTES; DateTime marketOpen = GetMarketOpenInMarketTimeZone(value); DateTime now = GetNowInMarketTimeZone(); DateTime preMarketStart = marketOpen.AddMinutes(-premarketMinutes); return now > preMarketStart && value.ToMarketTimeZone().ToUniversalTime() < marketOpen.ToUniversalTime(); } public static bool IsPostMarketWithTimeCheck(this DateTime value) { var postmarketMinutes = MarketHoursSettingsModel.POST_MARKET_MINUTES; DateTime marketClose = GetMarketCloseInMarketTimeZone(value); DateTime now = GetNowInMarketTimeZone(); DateTime postMarketEnd = marketClose.AddMinutes(postmarketMinutes); return now < postMarketEnd && value.ToMarketTimeZone().ToUniversalTime() >= marketClose.ToUniversalTime(); } public static DateTime RoundUp(this DateTime value, TimeSpan d) { if (d.Ticks == 0) { return value; } return new DateTime((value.Ticks + d.Ticks - 1) / d.Ticks * d.Ticks, value.Kind); } public static DateTime RoundDown(this DateTime value, TimeSpan d) { if (d.Ticks == 0) { return value; } return new DateTime((value.Ticks / d.Ticks) * d.Ticks); } public static DateTime StartOfWeek(this DateTime dt, DayOfWeek startOfWeek) { int diff = (7 + (dt.DayOfWeek - startOfWeek)) % 7; return dt.AddDays(-1 * diff).Date; } public static DateTime GetNowInMarketTimeZone() { DateTime nowEastern = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.UtcNow, MARKET_TIME_ZONE); return nowEastern; } /// /// Get Now in Local Time Zone. This takes into account the Current Time Zone setting in Options. /// /// public static DateTime GetNowInLocalTimeZone() { DateTime nowLocal = TimeZoneInfo.ConvertTime(DateTime.UtcNow, ServerFormats.CurrentTimeZone); return nowLocal; } /// /// Returns a DateTime object for the market open for a "generic" day. This is meant to show the correct DateTime /// on strategy configs even on holidays. /// /// public static DateTime GetMarketOpenInMarketTimeZone() { DateTime nowEastern = GetNowInMarketTimeZone(); DateTime marketOpenEastern = nowEastern.Date.AddMinutes(MarketHoursSettingsModel.MINUTES_UNTIL_MARKET_OPEN); return marketOpenEastern; } /// /// Returns a DateTime object for the market open. There are two versions of this function: one requires a DateTime parameter and one doesn't. /// This function is full day holidays and weekend aware. The one without the DateTime parameter is meant to return the market open for a "generic" day /// and not a specific one. /// /// /// public static DateTime GetMarketOpenInMarketTimeZone(DateTime date) { DateTime dateInMarketTimeZone = date.ToMarketTimeZone(); // Find next marketday. Take into account weekends and holidays. dateInMarketTimeZone = GetNextWeekday(dateInMarketTimeZone); while (HolidayManager.Instance().IsMarketClosed(dateInMarketTimeZone)) { // Find next marketday. Take into account weekends and holidays. dateInMarketTimeZone = GetNextWeekday(dateInMarketTimeZone); dateInMarketTimeZone = dateInMarketTimeZone.AddWeekDays(1); } DateTime marketOpenMarketTimeZone = dateInMarketTimeZone.Date.AddMinutes(MarketHoursSettingsModel.MINUTES_UNTIL_MARKET_OPEN); return marketOpenMarketTimeZone; } /// /// If the current date is a weekend day return the next weekday. /// /// /// private static DateTime GetNextWeekday(DateTime date) { if (date.DayOfWeek == DayOfWeek.Saturday) date = date.AddDays(2); if (date.DayOfWeek == DayOfWeek.Sunday) date = date.AddDays(1); return date; } /// /// Get Market Close Time In Market Time Zone. /// /// public static DateTime GetMarketCloseInMarketTimeZone() { DateTime nowEastern = GetNowInMarketTimeZone(); int minutesTillTodaysClose = MarketHoursSettingsModel.MINUTES_UNTIL_MARKET_OPEN + MarketHoursSettingsModel.MARKET_OPEN_MINUTES_FULL_DAY; if (HolidayManager.Instance().IsHalfDay(nowEastern)) minutesTillTodaysClose = MarketHoursSettingsModel.MINUTES_UNTIL_MARKET_OPEN + MarketHoursSettingsModel.MARKET_OPEN_MINUTES_HALF_DAY; DateTime marketCloseEastern = nowEastern.Date.AddMinutes(minutesTillTodaysClose); return marketCloseEastern; } /// /// Get Market Close Time In Market Time Zone. /// /// /// public static DateTime GetMarketCloseInMarketTimeZone(DateTime date) { DateTime dateInMarketTimeZone = date.ToMarketTimeZone(); int minutesTillTodaysClose = MarketHoursSettingsModel.MINUTES_UNTIL_MARKET_OPEN + MarketHoursSettingsModel.MARKET_OPEN_MINUTES_FULL_DAY; if (HolidayManager.Instance().IsHalfDay(date)) minutesTillTodaysClose = MarketHoursSettingsModel.MINUTES_UNTIL_MARKET_OPEN + MarketHoursSettingsModel.MARKET_OPEN_MINUTES_HALF_DAY; DateTime marketCloseMarketTimeZone = dateInMarketTimeZone.Date.AddMinutes(minutesTillTodaysClose); return marketCloseMarketTimeZone; } /// /// Adds a number of week days to a DateTime and returns a new DateTime. /// /// /// /// public static DateTime AddWeekDays(this DateTime date, int days) { if (days < 0) { throw new ArgumentException("days cannot be negative", "days"); } if (days == 0) return date; if (date.DayOfWeek == DayOfWeek.Saturday) { date = date.AddDays(2); days -= 1; } else if (date.DayOfWeek == DayOfWeek.Sunday) { date = date.AddDays(1); days -= 1; } date = date.AddDays(days / 5 * 7); int extraDays = days % 5; if ((int)date.DayOfWeek + extraDays > 5) extraDays += 2; return date.AddDays(extraDays); } /// /// Returns number of weekdays between two dates. /// /// /// /// public static int GetWeekDays(DateTime start, DateTime end) { if (start.DayOfWeek == DayOfWeek.Saturday) start = start.AddDays(2); else if (start.DayOfWeek == DayOfWeek.Sunday) start = start.AddDays(1); if (end.DayOfWeek == DayOfWeek.Saturday) end = end.AddDays(-1); else if (end.DayOfWeek == DayOfWeek.Sunday) end = end.AddDays(-2); int diff = (int)end.Subtract(start).TotalDays; int result = diff / 7 * 5 + diff % 7; if (end.DayOfWeek < start.DayOfWeek) return result - 2; else return result; } /// /// Get the market open time in the local time zone as set in the Options window. Now holiday aware. /// /// Use this date as a reference. For example, if you pass in a date time from yesterday the market open time for yesterday will be returned. /// Default reference time is today. /// public static DateTime GetMarketOpenLocalTime(DateTime? referenceDate = null) { // Here local time is the one based off the time zone set by user in the Options window. DateTime marketOpenEastern = GetMarketOpenInMarketTimeZone(); if (referenceDate.HasValue) marketOpenEastern = GetMarketOpenInMarketTimeZone(referenceDate.Value); TimeZoneInfo easternZone = TimeZoneInfo.FindSystemTimeZoneById(MARKET_TIME_ZONE); return GuiEnvironment.ConvertTimeCheckingKindProperty(marketOpenEastern, easternZone, ServerFormats.CurrentTimeZone); } /// /// Get the market close time in the local time zone as set in the Options window. Important: if referenceDate is not null /// then this function will check to see if referenceDate is a half day. If so, it will return 1pm ET for that day./(thi /// /// Use this date as a reference. For example, if you pass in a date time from yesterday the market close time for yesterday will be returned. /// Default reference time is today. /// public static DateTime GetMarketCloseLocalTime(DateTime? referenceDate = null) { int minutesInTodaysTradingDay = MarketHoursSettingsModel.MARKET_OPEN_MINUTES_FULL_DAY; if (referenceDate.HasValue && HolidayManager.Instance().IsHalfDay(referenceDate)) minutesInTodaysTradingDay = MarketHoursSettingsModel.MARKET_OPEN_MINUTES_HALF_DAY; DateTime marketCloseLocal = GetMarketOpenLocalTime(referenceDate).AddMinutes(minutesInTodaysTradingDay); return marketCloseLocal; } /// /// Get the pre market start time in the time zone selected by the user in the Options settings. /// /// /// public static DateTime GetPreMarketStartLocalTime(this DateTime value) { var premarketMinutes = MarketHoursSettingsModel.PRE_MARKET_MINUTES; DateTime marketOpen = GetMarketOpenInMarketTimeZone(value); DateTime preMarketStart = marketOpen.AddMinutes(-premarketMinutes); TimeZoneInfo easternZone = TimeZoneInfo.FindSystemTimeZoneById(MARKET_TIME_ZONE); return GuiEnvironment.ConvertTimeCheckingKindProperty(preMarketStart, easternZone, ServerFormats.CurrentTimeZone); } /// /// Get seconds until midnight in ET. /// /// public static int GetSecondsUntilMidnight() { DateTime marketClose = GuiEnvironment.GetMarketCloseInMarketTimeZone(); //DateTime nextMidnightEastern = marketClose.AddMinutes(8 * 60 + 1); // 480 + 1 int marketOpenFullDayMinutes = MarketHoursSettingsModel.MINUTES_UNTIL_MARKET_OPEN + MarketHoursSettingsModel.MARKET_OPEN_MINUTES_FULL_DAY; int fullDayMinutes = 24 * 60; int marketCloseUntilMidnightMinutes = (fullDayMinutes - marketOpenFullDayMinutes) + 1; // 480 + 1 DateTime nextMidnightEastern = marketClose.AddMinutes(marketCloseUntilMidnightMinutes); TimeSpan untilMidnight = nextMidnightEastern - GuiEnvironment.GetNowInMarketTimeZone(); return (int)untilMidnight.TotalSeconds; // for testing - now + 1 minute //return 60; } /* The items below require a connection to the data. Typically the GUI library can work * with any number of data connections. When you create an alert window, for example, you * have to specify the connection in the alert window's constructor. So we could have * multiple connections, some windows using one and some windows using another. * * The items below all use look up tables so you can find a shared GUI resource based on * the connection. For example, if two alert windows are using the same connection, then * they should share the same GeneralInfo object. A third window which uses a different * connection should use a different GeneralInfo object. */ // This property should only be accessed from the GUI thread. (The object you get // is thread safe, but this one request is not.) Ideally the main program will // initialize this object right away, so the data will be available to other // windows right away. However, we always create it the first time someone // requests it, so it's always safe to request it. The object is never replaced, // so it's safe to request a callback from the object. public static GeneralInfo GetGeneralInfo(ISendManager sendManager) { if (!_generalInfo.ContainsKey(sendManager)) { GeneralInfo result = new GeneralInfo(); result.RequestNow(sendManager); _generalInfo[sendManager] = result; return result; } else return _generalInfo[sendManager]; } private static Dictionary _generalInfo = new Dictionary(); public static SymbolListsCacheManager GetSymbolListsCacheManager(IConnectionMaster connectionMaster) { if (!_symbolListsCacheManager.ContainsKey(connectionMaster)) { _symbolListsCacheManager[connectionMaster] = new SymbolListsCacheManager(connectionMaster); } return _symbolListsCacheManager[connectionMaster]; } public static void ClearSymbolListsCacheManager() { _symbolListsCacheManager.Clear(); } private static Dictionary _symbolListsCacheManager = new Dictionary(); // These items are aimed at the layout. Generally you should pass a pointer to a ConnectionMaster // and not worry about its name. However, that doesn't work in the layout. You need to convert // the IConnectionMaster to a string and back. The layout manager does not know or care about // IConnectionMaster objects. The windows using a connection master need to do this conversion // to save and load themselves. // // Currently the main program only allows one ConnectionMaster, so this seems a bit extreme. // But we want the ability to have more than one ConnectionMaster. We are preserving that // ability in the library, even if it's not used yet. public static IConnectionMaster FindConnectionMaster(string name) { if (!_connectionMasterByName.ContainsKey(name)) // Let the caller decide what to do. Presumably the window will decide not to display // itself. We don't offer any sort of reasonable default, so there isn't much else // it could do. return null; return _connectionMasterByName[name]; } public static string ConnectionName(IConnectionMaster connectionMaster) { if (!_nameForConnectionMaster.ContainsKey(connectionMaster)) // Let the caller decide what to do. I can't imagine why this would happen, except // as a bug, so this might just cause an error down the line. return null; return _nameForConnectionMaster[connectionMaster]; } public static void Add(string name, IConnectionMaster connectionMaster) { // Duplicates are not allowed! _nameForConnectionMaster.Add(connectionMaster, name); _connectionMasterByName.Add(name, connectionMaster); } private static Dictionary _connectionMasterByName = new Dictionary(); private static Dictionary _nameForConnectionMaster = new Dictionary(); public static bool LimitedMode = false; public static QuoteFeedManager QuoteFeedManager; /// /// For debugging purposes. Controls whether to ignore all the formatting rules in DataGridViewSupport. /// You might want to do this to test performance. /// public static bool SimpleFormatting = false; /// /// When in Limited Mode this marks all menu items as enabled = false, otherwise marks them as enabled. /// If there is a MenuItem that DOES need to be disabled in normal mode you'll need to mark it that way /// after calling this function. /// /// The menu to act upon public static void EnforceLimitedMode(ContextMenuStrip menuStrip) { // currently this assumes everything is enabled in regular mode and disabled in limited mode with // a couple exceptions (Save to Cloud when logged in as demo and Graphical Indicator). in the future // if there are more and more exceptions we should probably convert all menu items to TIToolStripMenuItem // objects and add code to the TIToolStripMenuItem class that controls the enabled state when in different // modes. try { List menuItems = menuStrip.Items.OfType().ToList(); foreach (ToolStripMenuItem menuItem in menuItems) { TIToolStripMenuItem tiMenuItem = menuItem as TIToolStripMenuItem; if (null != tiMenuItem && GuiEnvironment.LimitedMode) tiMenuItem.Enabled = tiMenuItem.EnabledInLimitedMode; else if (null != menuItem.Tag && menuItem.Tag.ToString() == "SaveToCloud" && FindConnectionMaster("").LoginManager.IsDemo) menuItem.Enabled = false; else if (null != menuItem.Tag && menuItem.Tag.ToString() == "TimeFrame" && FindConnectionMaster("").LoginManager.AccountStatus != AccountStatus.Good) menuItem.Enabled = false; else if (null != menuItem.Tag && menuItem.Tag.ToString() == "false") menuItem.Enabled = false; else menuItem.Enabled = !GuiEnvironment.LimitedMode; } } catch (Exception e) { string debugView = e.ToString(); } } /// /// Returns a "friendly" string from a double. e.g. 1001 becomes 1.0K /// /// /// public static string ToFriendlyString(this double value) { DoubleFriendlyStringType type = GetDoubleFriendlyStringType(value); return value.ToFriendlyString(type); } public static DateTime AddBusinessDays(this DateTime date, int days) { if (days < 0) throw new ArgumentException("days cannot be negative", "days"); if (days == 0) return date; if (date.DayOfWeek == DayOfWeek.Saturday) { date = date.AddDays(2); days -= 1; } else if (date.DayOfWeek == DayOfWeek.Sunday) { date = date.AddDays(1); days -= 1; } date = date.AddDays(days / 5 * 7); int extraDays = days % 5; if ((int)date.DayOfWeek + extraDays > 5) { extraDays += 2; } return date.AddDays(extraDays); } /// /// Truncates a DateTime to a specified resolution. /// A convenient source for resolution is TimeSpan.TicksPerXXXX constants. /// /// The DateTime object to truncate /// e.g. to round to nearest second, TimeSpan.TicksPerSecond /// Truncated DateTime public static DateTime Truncate(this DateTime date, long resolution) { return new DateTime(date.Ticks - (date.Ticks % resolution), date.Kind); } /// /// In some cases we already know the friendly type that we want to use. For example, each yaxis label on the chart should /// use the same friendly type even if some of the values on their own would use a different type. /// /// The value. /// The type. /// public static string ToFriendlyString(this double value, DoubleFriendlyStringType type, string format = "N2") { string toReturn = value.ToString(format); if (type == DoubleFriendlyStringType.Billions) toReturn = (value / 1000000000).ToString(format) + "B"; else if (type == DoubleFriendlyStringType.Millions) toReturn = (value / 1000000).ToString(format) + "M"; else if (type == DoubleFriendlyStringType.Thousands) toReturn = (value / 1000).ToString(format) + "K"; else if (type == DoubleFriendlyStringType.SingleDecimal) toReturn = value.ToString("N1"); return toReturn; } /// /// A type for the conversion of doubles to friendly strings. /// public enum DoubleFriendlyStringType { Billions, Millions, Thousands, SingleDecimal, Normal, None } public static DoubleFriendlyStringType GetDoubleFriendlyStringType(this double value) { DoubleFriendlyStringType toReturn = DoubleFriendlyStringType.Normal; if (value > 1000000000) toReturn = DoubleFriendlyStringType.Billions; else if (value > 1000000) toReturn = DoubleFriendlyStringType.Millions; else if (value > 1000) toReturn = DoubleFriendlyStringType.Thousands; else if (value > 100) toReturn = DoubleFriendlyStringType.SingleDecimal; return toReturn; } /// /// Recursively iterates on controls on this control and returns a list of controls of specified type. Similar to Application.OpenForms but for /// controls inside a form or other control. /// /// /// /// public static IEnumerable GetAll(this Control control, Type type) { if (control == null) return new List(); var controls = control.Controls.Cast(); return controls.SelectMany(ctrl => GetAll(ctrl, type)).Concat(controls).Where(c => c.GetType() == type); } //This method has been updated.. since now more people are using multiple screens...There's now a second parameter for the parent form // Setting the parent to null will use the default location from the Save As default window. public static void SetWindowOpeningPosition(Form form, Form parent) { Screen screen = null; if (null != parent && !parent.IsDisposed) screen = Screen.FromControl(parent); if (screen != null) { int topOffset = 50; int leftOffset = 50; // We want channelbars and welcome screens to open differently. WelcomeScreen test = form as WelcomeScreen; if (test != null) { topOffset = 80; leftOffset = 0; if (GuiEnvironment.RunningWin10) leftOffset -= GuiEnvironment.WIDTH_INCREASE / 2; } form.Top = parent.Top + topOffset; form.Left = parent.Left + leftOffset; Rectangle workingRectangle = screen.WorkingArea; form.Location = new Point(parent.Left + leftOffset, parent.Top + topOffset); if (!workingRectangle.Contains(form.DesktopBounds)) form.Location = new Point(screen.Bounds.X + leftOffset, screen.Bounds.Y + topOffset); // set position relative to current monitor } } /// /// This function is similar to SetWindowOpeningPosition. But sets the new window location in the XML node /// so it can passed to the LayoutManager.Restore() /// /// XML node to modify. /// Parent form. public static void SetWindowOpeningPositionXML(XmlNode xmlNode, Form parent) { Screen screen = null; if (null != parent && !parent.IsDisposed) screen = Screen.FromControl(parent); if (screen != null) { int topOffset = 50; int leftOffset = 50; XmlNode baseNode = xmlNode.Node("WINDOW").Node("BASE"); if (baseNode != null) { baseNode.SetProperty("Top", parent.Top + topOffset); baseNode.SetProperty("Left", parent.Left + leftOffset); Rectangle workingRectangle = screen.WorkingArea; Rectangle windowRectangle = new Rectangle(baseNode.Property("Left", 0), baseNode.Property("Top", 0), baseNode.Property("Width", 0), baseNode.Property("Height", 0)); if (!workingRectangle.Contains(windowRectangle)) { baseNode.SetProperty("Top", screen.Bounds.Y + topOffset); baseNode.SetProperty("Left", screen.Bounds.X + leftOffset); } } } } private static ImageList _customChannelImageResourceList; public const int CHANNEL_IMAGE_WIDTH = 128; public const int CHANNEL_IMAGE_HEIGHT = 73; public static ImageList GetCustomChannelImageResourceList() { if (null == _customChannelImageResourceList) { // Load the full size images to image List _customChannelImageResourceList = new ImageList(); _customChannelImageResourceList.ColorDepth = ColorDepth.Depth32Bit; _customChannelImageResourceList.ImageSize = new Size(CHANNEL_IMAGE_WIDTH, CHANNEL_IMAGE_HEIGHT); _customChannelImageResourceList.Images.Add("ch_1", TradeIdeas.TIProGUI.Properties.Resources.ch_1); _customChannelImageResourceList.Images.Add("ch_2", TradeIdeas.TIProGUI.Properties.Resources.ch_2); _customChannelImageResourceList.Images.Add("ch_3", TradeIdeas.TIProGUI.Properties.Resources.ch_3); _customChannelImageResourceList.Images.Add("ch_4", TradeIdeas.TIProGUI.Properties.Resources.ch_4); _customChannelImageResourceList.Images.Add("ch_5", TradeIdeas.TIProGUI.Properties.Resources.ch_5); _customChannelImageResourceList.Images.Add("ch_6", TradeIdeas.TIProGUI.Properties.Resources.ch_6); _customChannelImageResourceList.Images.Add("ch_7", TradeIdeas.TIProGUI.Properties.Resources.ch_7); _customChannelImageResourceList.Images.Add("ch_8", TradeIdeas.TIProGUI.Properties.Resources.ch_8); _customChannelImageResourceList.Images.Add("ch_9", TradeIdeas.TIProGUI.Properties.Resources.ch_9); _customChannelImageResourceList.Images.Add("ch_10", TradeIdeas.TIProGUI.Properties.Resources.ch_10); _customChannelImageResourceList.Images.Add("ch_11", TradeIdeas.TIProGUI.Properties.Resources.ch_11); _customChannelImageResourceList.Images.Add("ch_12", TradeIdeas.TIProGUI.Properties.Resources.ch_12); } return _customChannelImageResourceList; } public static List UserChannelList = new List(); public static bool UserChannelListUpdateComplete = false; public static int UserSelectedChannel = -1; // Value of -1 is not selected. public static bool UserChannelRequest = false; public static void SetUserChannelList(List list) { UserChannelList.Clear(); foreach (UserChannelData item in list) { UserChannelList.Add(item); } } public static void GetUserChannelList() { // TODO: this is for testing purposes. Data should be received via the server. if (GuiEnvironment.UserChannelList.Count == 0) { //UserChannelList.Clear(); // Initialize fonts used for channel names. CustomChannelEditor.InitOratorStdFont(); string _userName = FindConnectionMaster("").LoginManager.Username; UserChannelData d = new UserChannelData(); UserChannelData e = new UserChannelData(); UserChannelData f = new UserChannelData(); UserChannelData g = new UserChannelData(); UserChannelData h = new UserChannelData(); d.id = Guid.NewGuid().ToString(); d.imageResourceName = "ch_2"; d.channelName = "MyChannel2"; d.cloudLink = "https://www.trade-ideas.com/Cloud.html?code=6548f22224c3623736686df71a6c5068"; //d.baseImage = getImage("http://will.trade-ideas.com/home/klewis/CustomChannelImages/a_base.png"); if (d.baseImage != null) { Image d_img = CustomChannelEditor.CreateDynamicInactiveChannel(d.baseImage, d.channelName); } if (d.imageResourceName != null && !d.imageResourceName.Equals("")) { Image d_img = CustomChannelEditor.CreateDynamicInactiveResourceImageChannel(d.imageResourceName, d.channelName); } e.id = Guid.NewGuid().ToString(); e.imageResourceName = "ch_1"; e.channelName = "MyChannel1"; e.cloudLink = "https://www.trade-ideas.com/Cloud.html?code=43a72dbcc77440fd98d6844feae50e88"; //e.baseImage = getImage("http://will.trade-ideas.com/home/klewis/CustomChannelImages/b_base.png"); if (e.baseImage != null) { Image e_img = CustomChannelEditor.CreateDynamicInactiveChannel(e.baseImage, e.channelName); } if (e.imageResourceName != null && !e.imageResourceName.Equals("")) { Image e_img = CustomChannelEditor.CreateDynamicInactiveResourceImageChannel(e.imageResourceName, e.channelName); } f.id = Guid.NewGuid().ToString(); f.imageResourceName = "ch_4"; f.channelName = "MyChannel3"; f.cloudLink = "https://www.trade-ideas.com/Cloud.html?code=bd399334ad7089f4872623ea839e834b"; //f.baseImage = getImage("http://will.trade-ideas.com/home/klewis/CustomChannelImages/c_base.png"); if (f.baseImage != null) { Image f_img = CustomChannelEditor.CreateDynamicInactiveChannel(f.baseImage, f.channelName); } if (f.imageResourceName != null && !f.imageResourceName.Equals("")) { Image f_img = CustomChannelEditor.CreateDynamicInactiveResourceImageChannel(f.imageResourceName, f.channelName); } g.id = Guid.NewGuid().ToString(); g.imageResourceName = "ch_3"; g.channelName = "MyChannel4"; g.cloudLink = "https://www.trade-ideas.com/Cloud.html?code=a12ee3128762bac02499fdb6b923d780"; //g.baseImage = getImage("http://will.trade-ideas.com/home/klewis/CustomChannelImages/d_base.png"); if (g.baseImage != null) { Image g_img = CustomChannelEditor.CreateDynamicInactiveChannel(g.baseImage, g.channelName); } if (g.imageResourceName != null && !g.imageResourceName.Equals("")) { Image g_img = CustomChannelEditor.CreateDynamicInactiveResourceImageChannel(g.imageResourceName, g.channelName); } h.id = Guid.NewGuid().ToString(); h.imageResourceName = "ch_5"; h.channelName = "MyChannel5"; h.cloudLink = "https://www.trade-ideas.com/Cloud.html?code=a12ee3128762bac02499fdb6b923d780"; //h.baseImage = getImage("http://will.trade-ideas.com/home/klewis/CustomChannelImages/d_base.png"); if (h.baseImage != null) { Image h_img = CustomChannelEditor.CreateDynamicInactiveChannel(h.baseImage, h.channelName); } if (h.imageResourceName != null && !h.imageResourceName.Equals("")) { Image h_img = CustomChannelEditor.CreateDynamicInactiveResourceImageChannel(h.imageResourceName, h.channelName); } UserChannelList.Add(d); UserChannelList.Add(e); // Only populate 2 channels for initial testing. //UserChannelList.Add(f); //UserChannelList.Add(g); //UserChannelList.Add(h); // Need to create sorted list //List SortedList = _userDat.OrderBy(o => o.channelOrder).ToList(); //UserChannelList.Sort((x, y) => x.channelOrder.CompareTo(y.channelOrder)); } } public static void LoadUserChannelList(XmlNode description) { CustomChannelEditor.InitOratorStdFont(); UserChannelList.Clear(); XmlNode userChannelNode = description.Node("USER_CHANNELS"); if (null != description) { foreach (XmlNode node in description.ChildNodes) { UserChannelData userChannelData = new UserChannelData(); userChannelData.id = Guid.NewGuid().ToString(); userChannelData.imageResourceName = node.Property("IMAGE_RESOURCE_NAME", ""); userChannelData.channelName = node.Property("CHANNEL_NAME", ""); userChannelData.cloudLink = node.Property("CLOUD_LINK", ""); userChannelData.baseImage = null; // Images will be loaded when we get the images. UserChannelList.Add(userChannelData); } } } public static Image GetTestImage() { Image image = null; foreach (UserChannelData item in UserChannelList) { if (null != item.baseImage) { image = (Image)item.baseImage.Clone(); break; } } return image; } // From https://stackoverflow.com/questions/3801275/how-to-convert-image-to-byte-array/16576471#16576471 // ImageConverter object used to convert byte arrays containing JPEG or PNG file images into // Bitmap objects. This is static and only gets instantiated once. private static readonly ImageConverter _imageConverter = new ImageConverter(); /// /// Method to "convert" an Image object into a byte array, formatted in PNG file format, which /// provides lossless compression. This can be used together with the GetImageFromByteArray() /// method to provide a kind of serialization / deserialization. /// /// Image object, must be convertible to PNG format /// byte array image of a PNG file containing the image public static byte[] CopyImageToByteArray(Image theImage) { using (MemoryStream memoryStream = new MemoryStream()) { theImage.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png); return memoryStream.ToArray(); } } /// /// Method that uses the ImageConverter object in .Net Framework to convert a byte array, /// presumably containing a JPEG or PNG file image, into a Bitmap object, which can also be /// used as an Image object. /// /// byte array containing JPEG or PNG file image or similar /// Bitmap object if it works, else exception is thrown public static Bitmap GetImageFromByteArray(byte[] byteArray) { Bitmap bm = (Bitmap)_imageConverter.ConvertFrom(byteArray); if (bm != null && (bm.HorizontalResolution != (int)bm.HorizontalResolution || bm.VerticalResolution != (int)bm.VerticalResolution)) { // Correct a strange glitch that has been observed in the test program when converting // from a PNG file image created by CopyImageToByteArray() - the dpi value "drifts" // slightly away from the nominal integer value bm.SetResolution((int)(bm.HorizontalResolution + 0.5f), (int)(bm.VerticalResolution + 0.5f)); } return bm; } //// new properties to hold Global Settings XML and Lists send from server or loaded from file - RVH20210527 public static List GlobalSettings = new List(); public static bool HollyAIAllowed() { return GuiEnvironment.GlobalSettings.Node("BROKERAGE_PLUS").Node("SHOW_HOLLY_AI").Property("VALUE", false); } // new method to fix setting of Form and Label Text property when containing special character - RVH20210716 // current characters fixed: & public static string FixFormLabelText(string inText) { string outText = inText; if (outText.Contains("&")) outText = outText.Replace("&", "&&"); return outText; } // new properties to persist read news article in the new Single Stock Dock Window - RVH20210716 public static List NewsArticlesRead = new List(); /// /// Add a news article to the read list. /// /// NewsData for article. public static void AddNewsArticleRead(EnhancedSingleStockWindow.NewsData newsArticleData) { if (NewsArticlesRead.Find(x => x.Link == newsArticleData.Link) == null) NewsArticlesRead.Add(newsArticleData); } /// /// Check to see if news article is read. /// /// NewsData for the article. /// public static bool IsNewsArticleRead(EnhancedSingleStockWindow.NewsData newsArticleData) { return NewsArticlesRead.Find(x => x.Link == newsArticleData.Link) != null; } /// /// Set this to true when the Symbol Lookup is display and selecting a symbol. /// Surf Manager will check this before starting. If true, surfing will not restart. /// public static bool CurrentlyLookingUpSymbol = false; /// /// The TIProSkunkWorksExtension project uses this variable to let the Top List know /// it can open the Real-Time Stock Race form. /// //public static bool RealTimeStockRaceEnabled = false; /// /// The TIProSkunkWorksExtension project uses this action to allow opening of the /// Real-Time Stock Race form. /// public static Action OpenRealTimeStockRace; //public static Action OpenRealTimeStockRace; /// /// Get an adjustment factor between the passed font and the default font. /// This factor is a percentage of the font height difference. /// /// /// public static double GetFontAdjustFactor(Font font) { double adjustFactor = 0; Font defaultFont = new Font(GuiEnvironment.DEFAULT_FONT, GuiEnvironment.DEFAULT_FONTSIZE, FontStyle.Regular, GraphicsUnit.Point); adjustFactor = Convert.ToDouble(font.Height) / Convert.ToDouble(defaultFont.Height); return adjustFactor; } /// /// Company Logo Cache. /// public static ConcurrentDictionary CompanyLogoCache = new ConcurrentDictionary(); /// /// The positions grid in Brokerage+ uses these actions to process web page body mouse clicks. /// public static List PositionsWebBodyMouseClickActions = new List(); /// /// Call Positions Web body Click Action. /// /// public static void CallPositionsWebBodyClickAction(IntPtr handle) { WebBodyClickAction webBodyClickAction = PositionsWebBodyMouseClickActions.Find(x => x.Handle == handle); if (webBodyClickAction != null) webBodyClickAction.ClickAction(); } /// /// Delete Positions Web body Click Action. /// /// public static void DeletePositionsWebBodyClickAction(IntPtr handle) { WebBodyClickAction webBodyClickAction = PositionsWebBodyMouseClickActions.Find(x => x.Handle == handle); if (webBodyClickAction != null) PositionsWebBodyMouseClickActions.Remove(webBodyClickAction); } /// /// The profit/loss charts in Brokerage+ uses these actions to process web page body mouse clicks. /// public static List ProfitLossWebBodyMouseClickActions = new List(); /// /// Call Profit/Loss Web body Click Action. /// /// public static void CallProfitLossWebBodyClickAction(IntPtr handle) { WebBodyClickAction webBodyClickAction = ProfitLossWebBodyMouseClickActions.Find(x => x.Handle == handle); if (webBodyClickAction != null) webBodyClickAction.ClickAction(); } /// /// Delete Profit/Loss Web body Click Action. /// /// public static void DeleteProfitLossWebBodyClickAction(IntPtr handle) { WebBodyClickAction webBodyClickAction = ProfitLossWebBodyMouseClickActions.Find(x => x.Handle == handle); if (webBodyClickAction != null) ProfitLossWebBodyMouseClickActions.Remove(webBodyClickAction); } #region Native Windows Calls /*In case of the OddsMaker result window, during its processing it remains on top of all forms using "BringToFront(), *even* those of other applications (e.g. visual studio) This is probably because it was spawned from the OddsMakerConfig-a *dialog* box. However in this case with the "show alert" option, "BringToFront" will bring the form to front, but only in front of other windows within the same application (in this case, the windows of TiPro.) It will still stay hidden behind other windows of other applications. As a result,used some native Windows calls ..such as "SetForegroundWindow"*/ [return: MarshalAs(UnmanagedType.Bool)] [DllImport("user32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)] public static extern bool SetForegroundWindow(IntPtr hwnd); [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] public static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); [DllImport("user32.dll")] static extern int GetWindowText(IntPtr hwnd, StringBuilder ss, int count); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern int GetWindowThreadProcessId(IntPtr handle, out int processId); //From https://stackoverflow.com/questions/7162834/determine-if-current-application-is-activated-has-focus /// /// Returns true if the current application has focus, false otherwise /// /// The active status of the application public static bool ApplicationIsActivated() { var activatedHandle = GetForegroundWindow(); if (activatedHandle == IntPtr.Zero) { return false; // No window is currently activated } var procId = Process.GetCurrentProcess().Id; int activeProcId; GetWindowThreadProcessId(activatedHandle, out activeProcId); return activeProcId == procId; } //From https://thecodeprogram.com/how-to-get-active-windows-with-c- /// /// Get the title of active window including from external applications /// /// The title of the active window public static string ActiveWindowTitle() { //Create the variable const int nChar = 256; StringBuilder ss = new StringBuilder(nChar); //Run GetForeGroundWindows and get active window informations //assign them into handle pointer variable IntPtr handle = IntPtr.Zero; handle = GetForegroundWindow(); if (GetWindowText(handle, ss, nChar) > 0) return ss.ToString(); else return ""; } #endregion //Create MarketDataClient public static void CreateMarketDataClient() { MarketDataClient.Create(GlobalSettings.Node("MARKET_DATA").Node("SERVICE_ENDPOINT").Text("bws.trade-ideas.com/BrokersWebService/v1"), GlobalSettings.Node("MARKET_DATA").Node("TARGET_GROUP").Text(), GlobalSettings.Node("MARKET_DATA").Node("API_KEY").Text(), Convert.ToInt32(GlobalSettings.Node("MARKET_DATA").Node("THROTTLE_MS").Text("750")), GlobalSettings.Node("MARKET_DATA").Property("ENABLE_UI", true)); } } /// /// Web Body Click Action. /// public class WebBodyClickAction { public Action ClickAction; public IntPtr Handle; } //// public classes for Global Settings - RVH20210527 //public class DataGridColumnSetting //{ // public string FormType; // public string Code; // public string DisplayName; // public int PreferredWidth; // public int MinimumWidth; // public string Format; //} //public class DataGridSetting //{ // public string FormType; // public string FillMode; //} //public enum DataGridFillMode //{ // SETCOLUMNSEVENLY //} public static class DrawUtils { public static void DrawRoundedRectangleGradient(this Graphics g, Color topColor, Color bottomColor, Rectangle rec, int radius, RoundedCorners corners) { using (var b = new System.Drawing.Drawing2D.LinearGradientBrush(rec, topColor, bottomColor, 90)) { int x = rec.X; int y = rec.Y; int diameter = radius * 2; var horiz = new Rectangle(x, y + radius, rec.Width, rec.Height - diameter); var vert = new Rectangle(x + radius, y, rec.Width - diameter, rec.Height); g.FillRectangle(b, horiz); g.FillRectangle(b, vert); if ((corners & RoundedCorners.TopLeft) == RoundedCorners.TopLeft) g.FillEllipse(b, x, y, diameter, diameter); else g.FillRectangle(b, x, y, diameter, diameter); if ((corners & RoundedCorners.TopRight) == RoundedCorners.TopRight) g.FillEllipse(b, x + rec.Width - (diameter + 1), y, diameter, diameter); else g.FillRectangle(b, x + rec.Width - (diameter + 1), y, diameter, diameter); if ((corners & RoundedCorners.BottomLeft) == RoundedCorners.BottomLeft) g.FillEllipse(b, x, y + rec.Height - (diameter + 1), diameter, diameter); else g.FillRectangle(b, x, y + rec.Height - (diameter + 1), diameter, diameter); if ((corners & RoundedCorners.BottomRight) == RoundedCorners.BottomRight) g.FillEllipse(b, x + rec.Width - (diameter + 1), y + rec.Height - (diameter + 1), diameter, diameter); else g.FillRectangle(b, x + rec.Width - (diameter + 1), y + rec.Height - (diameter + 1), diameter, diameter); } } public static void DrawRoundedRectangle(this Graphics g, Color color, Rectangle rec, int radius, RoundedCorners corners) { using (var b = new SolidBrush(color)) { int x = rec.X; int y = rec.Y; int diameter = radius * 2; var horiz = new Rectangle(x, y + radius, rec.Width, rec.Height - diameter); var vert = new Rectangle(x + radius, y, rec.Width - diameter, rec.Height); g.FillRectangle(b, horiz); g.FillRectangle(b, vert); if ((corners & RoundedCorners.TopLeft) == RoundedCorners.TopLeft) g.FillEllipse(b, x, y, diameter, diameter); else g.FillRectangle(b, x, y, diameter, diameter); if ((corners & RoundedCorners.TopRight) == RoundedCorners.TopRight) g.FillEllipse(b, x + rec.Width - (diameter + 1), y, diameter, diameter); else g.FillRectangle(b, x + rec.Width - (diameter + 1), y, diameter, diameter); if ((corners & RoundedCorners.BottomLeft) == RoundedCorners.BottomLeft) g.FillEllipse(b, x, y + rec.Height - (diameter + 1), diameter, diameter); else g.FillRectangle(b, x, y + rec.Height - (diameter + 1), diameter, diameter); if ((corners & RoundedCorners.BottomRight) == RoundedCorners.BottomRight) g.FillEllipse(b, x + rec.Width - (diameter + 1), y + rec.Height - (diameter + 1), diameter, diameter); else g.FillRectangle(b, x + rec.Width - (diameter + 1), y + rec.Height - (diameter + 1), diameter, diameter); } } } public enum RoundedCorners { None = 0x00, TopLeft = 0x02, TopRight = 0x04, BottomLeft = 0x08, BottomRight = 0x10, All = 0x1F } /// /// This is an interface for modifying individual alerts. A plugin can add, change, or delete columns and add, modify, or delete /// RowData objects as they stream from the server. /// public interface IRowDataExtension { // Any changes to this interface will break the TIProExtensionCSVLogger extension that uses it. // Even adding a new function to this interface will cause that extension not to load. // If we add a new function here, we'll need to recompile the TIProExtensionCSVLogger project // which exists outside of the TIPro solution and give this user a new version of the extension // and a new version of TIPro at the same time. ToolStripMenuItem GetMenuItem(object parent); IList HandleColumnInfo(object parent, IList columns); List HandleRowData(object parent, string windowName, IList columns, List rowDatas); void SaveToLayout(object parent, XmlNode description); void RestoreFromLayout(object parent, XmlNode description); string GetId(); } /// /// This class is used to find the icon associated with a window. This class looks in the config /// file so you can override the icon for a particular window. This allows us to customize the /// application for people, i.e. a "white label". /// /// The preferred way to find the icon for a window is to say [class name].WindowIconCache.Icon. /// Note that WindowIconCache is a *static* property of most of our window classes. So you can /// read this before implementing an instance of the window. For example, the menu that allows /// you to open an alert window might be displayed before we create any alert windows. /// /// Note that each icon has a name. This is required so we can reference the icons in the XML /// config file. This is also used by the cloud layout system. We might save the icon for a /// window into the cloud. While reviewing what's in the cloud, we can rebuild the icon for that /// item. /// /// This is called a "cache" because we try to find each icon only once. It is best to retrieve /// this object from the window class each time, rather than to create a new WindowIconCache /// each time because each new instance of WindowIconCache will have to find the icon again. /// /// This class will try to provide a default icon if it can't find the requested icon. Note /// that the default icon is also configurable. (It is the icon for the main window.) The /// idea is as follows. We create a new version of TI pro for Eventus and customize many of the /// icons for them. Then we create a new window with a new icon. Eventus does not have a /// custom icon for this window. They will get the main icon for their app in this window. /// So the color scheme will match. If they got the icon that we use in the normal TI Pro, the /// colors would be wrong. /// public class WindowIconCache { private readonly string _windowName; private bool _cacheInitialized; private Icon _cachedIcon; public WindowIconCache(string windowName = "DEFAULT") { _windowName = windowName; } /// /// This is the name used in the constructor. If you want to save this object /// and recreate it later, save this name. Note that this is the name of the /// class, not the instance, of the window. /// public string WindowName { get { return _windowName; } } public void SetIcon(Form form, bool rememberThisOne = false) { LoadCache(); if (null != _cachedIcon) form.Icon = _cachedIcon; else if (rememberThisOne) // This is mostly aimed at the main form. If we can't find an icon // by the normal methods, then we save the one we already had so // other forms can use it. _cachedIcon = form.Icon; } private void LoadCache() { if (_cacheInitialized) return; try { XmlNode node = GuiEnvironment.XmlConfig.Node("WINDOW_ICONS").Node(_windowName).Select(); string method = node.Property("METHOD"); string baseName = node.Text(null); if (method == "internal") { // Load from a a resource. Type form = GetType(baseName); if (null != form) { // By default we look for the same icon that gets installed by the normal constructor. // If you add an icon to a form in the form builder, this is where it will go. string which = node.Property("WHICH", "$this.Icon"); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(form); _cachedIcon = (System.Drawing.Icon)(resources.GetObject(which)); } } else { // Load from file if (null != baseName) _cachedIcon = new Icon(Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), baseName)); } } catch { } if ((null == _cachedIcon) && (null != GuiEnvironment.DefaultIcon) && (this != GuiEnvironment.DefaultIcon)) _cachedIcon = GuiEnvironment.DefaultIcon.Icon; // I wish I could do this. // if (null == _cachedIcon) _cachedIcon = application.mainForm.icon; _cacheInitialized = true; } private static Type GetType(string name) { foreach (System.Reflection.Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) { Type result = assembly.GetType(name); if (null != result) return result; } return null; } public Icon Icon { get { LoadCache(); return _cachedIcon; } set { // The default way to get the icon is in LoadCache(). But for flexibility we allow people // to set it from other sources. _cachedIcon = value; _cacheInitialized = true; } } /// /// Converts the icon to a bitmap of the appropriate size. Note that icons are designed to have /// multiple sizes. By converting it this way, instead of converting to a bitmap and then reszing /// it, you can often get a better quality image. /// /// Desired size of the output. /// The image converted to a bitmap. public Bitmap GetBitmap(Size size) { if (null == Icon) // This is the best we can do. And it's probably okay. If we are going to set a property // to null, that probably means to use the default. return null; return new Icon(Icon, size).ToBitmap(); } /// /// This returns a 16x16 image, suitable for a menu. Note that icons are designed to have /// multiple sizes. By converting it this way, instead of converting to a bitmap and then resizing /// it, you can often get a better quality image. /// public Bitmap MenuImage { get { return GetBitmap(MENU_SIZE); } } private static Size MENU_SIZE = new Size(16, 16); } /// /// Some windows automatically change their names at various times. In particular, a lot of them /// will change their name when we log in or switch to DEMO mode. This interface says that a /// window wants to know when we change modes. /// public interface IDemoMode { void OnDemoModeChanged(); } //here we need to know accountstatus (namely for oddsmaker) so that we can determine whether //oddsmaker is available amd thus rbi/gbi window. public interface IAccountStatusChanged { void onAccountStatusChanged(); } // Interface to clear the data from TI windows // This interface will call the Clear context menu item public interface IClearData { void onClearData(); } /// /// This interface is used to call custom cleanup functions before shutting down the parent or application. /// public interface ICleanup { void Cleanup(); } /// /// Interface for handling candle data as it relates to trading. /// public interface ICandleDataHandler { /// /// User right clicks on specific candle and chooses this handler. This function is called with the RowData from the candle. /// /// The candle data. void HandleCandleData(Charts.TIDataPoint candleData, List candles); /// /// Called when a chart has completed a candle. /// /// The completed candle. /// The candles. void HandleCompletedCandle(Charts.TIDataPoint completedCandle, List candles); /// /// Gets the description for this candle data handler. /// /// The candle data. /// True = click sends trade, False = click schedules trade. /// /// Description to include on menu item /// string GetHandlerDescription(Charts.TIDataPoint candleData, bool clickHandler); } public class QuickButtonSetup { public string ID = Guid.NewGuid().ToString(); public string ButtonName; public Color BackgroundColor = Color.White; public Color TextColor = Color.Black; public string OrderApplyTo = "Symbol Linking"; public decimal OrderQuantity; public string OrderQuantityUnits = "Dollars"; public string OrderSide = "Buy"; public string OrderType = "Market"; public string OrderDefaultPrice = "Last"; public decimal OrderLimitPriceOffset; public string TIFSetting = "Day"; public int TIFCustomValue; public string TIFCustomIncrement = "Seconds"; public bool TIFFillAfterHours; public int TimeStopMinutes; public string TimeStopSetting = "After Open"; public int TimeStopDays; public bool StopLoss; public decimal StopLossValue; public string StopLossType = "$"; public bool ProfitTarget; public decimal ProfitTargetValue; public string ProfitTargetType = "$"; public bool DefaultButton; public QuickButtonSetup Copy() { QuickButtonSetup quickButtonSetupCopy = new QuickButtonSetup(); quickButtonSetupCopy.ID = Guid.NewGuid().ToString(); quickButtonSetupCopy.ButtonName = this.ButtonName; quickButtonSetupCopy.BackgroundColor = this.BackgroundColor; quickButtonSetupCopy.TextColor = this.TextColor; quickButtonSetupCopy.OrderApplyTo = this.OrderApplyTo; quickButtonSetupCopy.OrderQuantity = this.OrderQuantity; quickButtonSetupCopy.OrderQuantityUnits = this.OrderQuantityUnits; quickButtonSetupCopy.OrderSide = this.OrderSide; quickButtonSetupCopy.OrderType = this.OrderType; quickButtonSetupCopy.OrderDefaultPrice = this.OrderDefaultPrice; quickButtonSetupCopy.TIFSetting = this.TIFSetting; quickButtonSetupCopy.TIFCustomValue = this.TIFCustomValue; quickButtonSetupCopy.TIFCustomIncrement = this.TIFCustomIncrement; quickButtonSetupCopy.TIFFillAfterHours = this.TIFFillAfterHours; quickButtonSetupCopy.TimeStopMinutes = this.TimeStopMinutes; quickButtonSetupCopy.TimeStopSetting = this.TimeStopSetting; quickButtonSetupCopy.TimeStopDays = this.TimeStopDays; quickButtonSetupCopy.StopLoss = this.StopLoss; quickButtonSetupCopy.StopLossValue = this.StopLossValue; quickButtonSetupCopy.StopLossType = this.StopLossType; quickButtonSetupCopy.ProfitTarget = this.ProfitTarget; quickButtonSetupCopy.ProfitTargetValue = this.ProfitTargetValue; quickButtonSetupCopy.ProfitTargetType = this.ProfitTargetType; quickButtonSetupCopy.DefaultButton = this.DefaultButton; return quickButtonSetupCopy; } } public class TradeRequirementsSettings { public bool IsBrokerConnected { get; set; } public bool ChartLongStrategyConfigured { get; set; } public bool ChartShortStrategyConfigured { get; set; } public bool IsBPWindowPresent { get; set; } public bool IsAccountLongConfigured { get; set; } public bool IsAccountShortConfigured { get; set; } public string ShortAccountName { get; set; } = string.Empty; public string LongAccountName { get; set; } = string.Empty; public void SetAllFalse() { IsBPWindowPresent = false; ChartLongStrategyConfigured = false; ChartShortStrategyConfigured = false; IsBrokerConnected = false; IsAccountLongConfigured = false; IsAccountShortConfigured = false; ShortAccountName = string.Empty; LongAccountName = string.Empty; } public bool AllRequirementsMet => AllShortRequirements && AllLongRequirements; public bool AllShortRequirements => IsAccountShortConfigured && ChartShortStrategyConfigured && _otherRequirements; public bool AllLongRequirements => IsAccountLongConfigured && ChartLongStrategyConfigured && _otherRequirements; private bool _otherRequirements => IsBrokerConnected && IsBPWindowPresent; } public class ChartTradeAccount { public int UserAccountId { get; set; } public string DisplayName { get; set; } public string Name { get; set; } public double RealizedProfit { get; set; } } public static class MarketHoursSettingsModel { // Default the values to current Market Hours, they will be overriden by the SettingsClient call public static string MARKET { get; set; } = "us_equities"; public static int MINUTES_UNTIL_MARKET_OPEN { get; set; } = 570; // (9 * 60) + 30 public static int MARKET_OPEN_MINUTES_FULL_DAY { get; set; } = 390; // (6 * 60) + 30 public static int MARKET_OPEN_MINUTES_HALF_DAY { get; set; } = 210; // (3 * 60) + 30 public static int PRE_MARKET_MINUTES { get; set; } = 330; // (5 * 60) + 30 public static int POST_MARKET_MINUTES { get; set; } = 240; // (4 * 60) public static void Initialize() { var client = new SettingsClient(GuiEnvironment.GlobalSettings.Node("MARKET_DATA").Node("SERVICE_ENDPOINT").Text("bws.trade-ideas.com/BrokersWebService/v1")); var settings = client.GetMarketHours(); var setting = settings.FirstOrDefault(); if (setting == null) return; MARKET = setting.Market; MINUTES_UNTIL_MARKET_OPEN = setting.MinutesUntilOpen; MARKET_OPEN_MINUTES_FULL_DAY = setting.FullDayOpenMinutes; MARKET_OPEN_MINUTES_HALF_DAY = setting.HalfDayOpenMinutes; PRE_MARKET_MINUTES = setting.PreMarketMinutes; POST_MARKET_MINUTES = setting.PostMarketMinutes; } public static string GetMarketOpenTimeLabel() { var totalMinutes = MINUTES_UNTIL_MARKET_OPEN; var today = DateTime.Today.AddMinutes(totalMinutes); return today.ToString("h:mm tt"); } public static string GetMarketCloseTimeLabel() { var totalMinutes = MINUTES_UNTIL_MARKET_OPEN + MARKET_OPEN_MINUTES_FULL_DAY; ; var today = DateTime.Today.AddMinutes(totalMinutes); return today.ToString("h:mm tt"); } } public static class NullableDateTimeExtensions { public static bool DoesNotHaveValue(this DateTime? value) { return !value.HasValue; } } public static class ListExtensions { public static bool NotNullOrEmpty(this IEnumerable source) { return source != null && source.Any(); } public static bool HasAny(this IEnumerable source) { return NotNullOrEmpty(source); } public static bool DoesNotHaveAny(this IEnumerable source) { return !NotNullOrEmpty(source); } } }