NinjaTrader + OilPriceAPI

The physical market, drawn on your CL and NG charts

Your exchange feed shows every tick of the futures contract and nothing about the physical market behind it. WTI spot assessments, the Waha differential, Western Canadian Select, EIA report cadence - none of it is in the feed. NinjaScript is unsandboxed C#, so an indicator can pull all of it from a REST API and draw it on the chart. This page is the complete wiring.

NinjaTrader® is a trademark of its owner. OilPriceAPI is not affiliated with or endorsed by NinjaTrader; this page documents calling our API from their platform's scripting layer.

1. A spot-plus-basis indicator in one file

Paste this into a new NinjaScript indicator (New > NinjaScript Editor > Indicator), compile, and add it to a CL or MCL chart. Add Newtonsoft.Json.dll under References once (right-click in the editor - the DLL ships with NinjaTrader). On intraday charts (bars of 30 minutes or less) it fetches the WTI spot assessment every 30 minutes and prints spot and the chart-minus-spot basis in the corner of the chart.

// NinjaScript indicator: WTI physical spot + basis vs your CL chart.
// NinjaScript is unsandboxed C#, so standard .NET HTTP works. Polling
// every 30 minutes is 48 requests/day - inside the free tier's 50/day.
// ONE-TIME SETUP: NinjaScript Editor > right-click > References > add
// Newtonsoft.Json.dll (it ships in NinjaTrader's bin folder).
using System;
using System.Net.Http;
using Newtonsoft.Json.Linq;
using NinjaTrader.NinjaScript;
using NinjaTrader.NinjaScript.DrawingTools;

namespace NinjaTrader.NinjaScript.Indicators
{
    public class OilPriceApiSpotBasis : Indicator
    {
        private static readonly HttpClient Http = new HttpClient();
        private DateTime lastFetchUtc = DateTime.MinValue;
        private double spot = double.NaN;

        [NinjaScriptProperty]
        public string ApiKey { get; set; }

        [NinjaScriptProperty]
        public string Code { get; set; } // e.g. WTI_CRUDE_USD

        protected override void OnStateChange()
        {
            if (State == State.SetDefaults)
            {
                Name = "OilPriceApiSpotBasis";
                Code = "WTI_CRUDE_USD";
                IsOverlay = true;
                Calculate = Calculate.OnBarClose;
            }
        }

        protected override void OnBarUpdate()
        {
            // 30-minute throttle: 48 calls/day, free tier allows 50/day.
            if ((DateTime.UtcNow - lastFetchUtc).TotalMinutes >= 30)
            {
                lastFetchUtc = DateTime.UtcNow;
                FetchSpotAsync();
            }

            if (!double.IsNaN(spot) && CurrentBar > 0)
            {
                double basis = Close[0] - spot;
                Draw.TextFixed(this, "opa_spot",
                    string.Format("{0} spot {1:F2}  |  chart-minus-spot {2:+0.00;-0.00}",
                        Code, spot, basis),
                    TextPosition.TopRight);
            }
        }

        private async void FetchSpotAsync()
        {
            try
            {
                var req = new HttpRequestMessage(HttpMethod.Get,
                    "https://api.oilpriceapi.com/v1/prices/latest?by_code=" + Code);
                req.Headers.TryAddWithoutValidation("Authorization", "Token " + ApiKey);
                var resp = await Http.SendAsync(req);
                var body = await resp.Content.ReadAsStringAsync();
                // Response envelope: {"status":"success","data":{"price":82.13,
                //   "code":"...","currency":"USD","created_at":"..."}}
                var json = JObject.Parse(body);
                if ((string)json["status"] == "success")
                    spot = (double)json["data"]["price"];
            }
            catch (Exception e)
            {
                Print("OilPriceAPI fetch failed: " + e.Message);
            }
        }
    }
}

The 30-minute throttle is deliberate: 48 requests a day sits inside the free tier's 50-requests-per-day window, so the indicator runs indefinitely on a free key. The budget is per key, not per indicator: two instances doubles the calls, so give a second chart a slower throttle or a paid key. Paid tiers poll as often as their request budget allows.

2. Codes that pair with futures charts

Every code below is served by the same endpoint - swap the Code property on the indicator, or run several instances on one chart.

API codePairs withWhat it is
WTI_CRUDE_USDCL / MCLWTI spot assessment - the physical side of your futures chart
BRENT_CRUDE_USDBRN, Brent-WTI spread workBrent spot, multi-source
NATURAL_GAS_USDNG / MNGUS natural gas benchmark in $/MMBtu
NATURAL_GAS_WAHANG basis tradesWaha hub - the Permian differential that can go negative
WCS_CRUDE_USDCL spread contextWestern Canadian Select - the heavy-crude differential

The interesting number is usually the difference, not the level: a CL chart with WTI spot drawn on it shows the physical premium or discount directly, and a Waha overlay on NG shows when the Permian basis detaches from Henry Hub - it has traded negative while the futures stayed positive.

3. EIA report days, without alt-tabbing

The weekly EIA petroleum status report (Wednesdays 10:30 ET) and natural gas storage report (Thursdays 10:30 ET) move CL and NG more than most technical levels. Our WTI and natural gas pages document how the physical series behave around those releases, and the same API serves EIA-derived series you can overlay the same way as the code above.

4. Want this as a packaged add-on instead of source code

We are gauging whether to ship a packaged NinjaTrader add-on - EIA report markers, spot overlays, and basis panels with no C# involved. If you would install that, tell us in one line. Enough hands raised is exactly how it gets built.

Start with a free key

50 requests a day, no card. The indicator above runs on it indefinitely.

Get an API key