// +------------------------------------------------------------------+ // | FXMacroData_MacroPanel.cs | // | Live macro panel indicator for NinjaTrader 8 | // | https://fxmacrodata.com | // +------------------------------------------------------------------+ // // Shows Policy Rate, Inflation, Unemployment and GDP for both // currencies of the active chart instrument in a fixed panel. // // INSTALLATION // 1. Copy FXMacroDataService.cs and this file to: // Documents\NinjaTrader 8\bin\Custom\Indicators\ // 2. In NinjaTrader open Tools > New NinjaScript Editor, then // File > Compile All. Both files must compile with 0 errors. // 3. Add "FXMacroData_MacroPanel" from Indicators to any FX chart. // 4. In the Inputs tab enter your API key (leave empty for USD free tier). // // DOCUMENTATION https://fxmacrodata.com/documentation/ninjatrader // API KEY https://fxmacrodata.com/subscribe // +------------------------------------------------------------------+ #region Using declarations using System; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Windows.Media; using NinjaTrader.Gui; using NinjaTrader.Gui.Chart; using NinjaTrader.NinjaScript; using NinjaTrader.Core.FloatingPoint; #endregion namespace NinjaTrader.NinjaScript.Indicators { [Description("FXMacroData live macro panel — Policy Rate, Inflation, Unemployment and GDP for the active pair.")] public class FXMacroData_MacroPanel : Indicator { // ── Private state ────────────────────────────────────────────────────── private DateTime _lastFetch = DateTime.MinValue; private string _panelText = string.Empty; // ── NinjaScript lifecycle ────────────────────────────────────────────── protected override void OnStateChange() { if (State == State.SetDefaults) { Description = "FXMacroData live macro panel — Policy Rate, Inflation, Unemployment, GDP."; Name = "FXMacroData_MacroPanel"; Calculate = Calculate.OnBarClose; IsOverlay = true; DisplayInDataBox = false; // Default input values ApiKey = string.Empty; BaseOverride = string.Empty; QuoteOverride = string.Empty; RefreshMinutes = 60; FontSize = 11; } } protected override void OnBarUpdate() { // Only act on the most recent bar if (CurrentBar < 1 || !BarsArray[0].IsFirstBarOfSession && CurrentBar != BarsArray[0].Count - 1) return; // Throttle API calls to RefreshMinutes interval if ((DateTime.UtcNow - _lastFetch).TotalMinutes < RefreshMinutes) return; _lastFetch = DateTime.UtcNow; FetchAndDraw(); } // ── Data fetching and display ────────────────────────────────────────── private void FetchAndDraw() { // Detect currencies from the instrument name (e.g. "EUR/USD", "EURUSD") string raw = Instrument.FullName.Replace("/", "").ToLower(); string cBase = !string.IsNullOrEmpty(BaseOverride) ? BaseOverride.ToLower() : (raw.Length >= 3 ? raw.Substring(0, 3) : "usd"); string cQuote = !string.IsNullOrEmpty(QuoteOverride) ? QuoteOverride.ToLower() : (raw.Length >= 6 ? raw.Substring(3, 3) : "eur"); string key = ApiKey ?? string.Empty; var sb = new System.Text.StringBuilder(); AppendCurrencyBlock(sb, cBase.ToUpper(), cBase, key); sb.AppendLine(); AppendCurrencyBlock(sb, cQuote.ToUpper(), cQuote, key); _panelText = sb.ToString(); Draw.TextFixed( this, "fmd_panel", _panelText, TextPosition.TopLeft, Brushes.White, new SimpleFont("Courier New", FontSize), Brushes.Transparent, Brushes.Black, 70); } private void AppendCurrencyBlock(System.Text.StringBuilder sb, string label, string ccy, string apiKey) { double rate = NinjaTrader.Custom.FXMacroDataService.LastValue( NinjaTrader.Custom.FXMacroDataService.GetIndicator(ccy, "policy_rate", apiKey)); double cpi = NinjaTrader.Custom.FXMacroDataService.LastValue( NinjaTrader.Custom.FXMacroDataService.GetIndicator(ccy, "inflation", apiKey)); double uemp = NinjaTrader.Custom.FXMacroDataService.LastValue( NinjaTrader.Custom.FXMacroDataService.GetIndicator(ccy, "unemployment", apiKey)); double gdp = NinjaTrader.Custom.FXMacroDataService.LastValue( NinjaTrader.Custom.FXMacroDataService.GetIndicator(ccy, "gdp", apiKey)); sb.AppendLine(string.Format("── {0} ──────────────", label)); sb.AppendLine(string.Format(" Policy Rate : {0}", Fmt(rate))); sb.AppendLine(string.Format(" Inflation : {0}", Fmt(cpi))); sb.AppendLine(string.Format(" Unemployment : {0}", Fmt(uemp))); sb.AppendLine(string.Format(" GDP : {0}", Fmt(gdp))); } private static string Fmt(double v) { return double.IsNaN(v) ? "n/a" : v.ToString("0.##") + "%"; } // ── Input properties ─────────────────────────────────────────────────── [NinjaScriptProperty] [Display(Name = "API Key", Order = 1, GroupName = "FXMacroData", Description = "Your FXMacroData API key. Leave empty to use the free USD tier.")] public string ApiKey { get; set; } [NinjaScriptProperty] [Display(Name = "Base Currency Override", Order = 2, GroupName = "FXMacroData", Description = "Force the base currency code (e.g. 'eur'). Leave empty to auto-detect from the instrument.")] public string BaseOverride { get; set; } [NinjaScriptProperty] [Display(Name = "Quote Currency Override", Order = 3, GroupName = "FXMacroData", Description = "Force the quote currency code (e.g. 'usd'). Leave empty to auto-detect from the instrument.")] public string QuoteOverride { get; set; } [NinjaScriptProperty] [Range(1, 1440)] [Display(Name = "Refresh Interval (minutes)", Order = 4, GroupName = "FXMacroData", Description = "How often to call the FXMacroData API. Minimum 1 minute.")] public int RefreshMinutes { get; set; } [NinjaScriptProperty] [Range(6, 24)] [Display(Name = "Font Size", Order = 5, GroupName = "FXMacroData", Description = "Panel font size in points.")] public int FontSize { get; set; } } }