In NinjaScript, the built-in tools like Series<T>
and Values[]
are powerful — but sometimes you need more control over your data.
Maybe you want to:
- Track a custom sequence of highs and lows 🔢
- Store session-specific information 📅
- Manage dynamic conditions across multiple bars or timeframes 🔄
That’s where Lists, Dictionaries, and Arrays come in. These C# structures give you a flexible, powerful way to handle historical data exactly the way you need for advanced custom indicators and strategies.
In this guide, we’ll break down:
- 📋 How Lists help manage growing, changing data
- 🗂️ How Dictionaries store and recall custom information easily
- 🔢 How Arrays offer super-fast, fixed-size data storage
By the end, you’ll know when to use each — and how to avoid common mistakes that trip up even experienced NinjaScript coders.
Let’s dive in! 🚀
📚 Related NinjaScript Lessons So Far
If you’re new here, make sure you check out the earlier posts too:
- Understanding the NinjaTrader Indicator Structure
- Variables and Properties in NinjaScript
- OnStateChange() Lifecycle Explained
- OnBarUpdate() Fully Explained
- Learn how to use Series<T>
- Learn NinjaScript Plots
- Learn NinjaScript Lines
- How to use NinjaScript Brushes
- Creating and Using Methods & Functions
- Add-On Your Way to Cleaner Code
🔢 Arrays in NinjaScript
🧱 What Is an Array?
At its simplest, an array is like a row of boxes 📦📦📦📦📦.
Each box can hold one value — and each box is numbered starting at 0.
You use arrays when you want to store a group of values of the same type (like a group of numbers, booleans, or text).
In NinjaScript (and C#), here’s how you create an array that holds five decimal numbers (called doubles):
double[] recentHighs = new double[5];
✅ Key Points:
double[]
→ The array will hold doubles (decimal numbers)recentHighs
→ This is the name you gave the arraynew double[5]
→ You are making five slots (positions 0 to 4)
🏗️ How Arrays Work
Every array:
- Has fixed size — once created, it cannot grow or shrink
- Has indexes — numbering starts at 0
- Stores only one type of value (all doubles, or all ints, etc.)
Example:
If you write recentHighs[2] = 4200.5;
, you’re putting the number 4200.5 into the third slot (index 2).
🚀 Step 1: Create and Fill an Array (First Values)
When you first create an array, all the slots are empty (default value = 0.0
for doubles).
Before you can “shift” or “compare” anything, you need to fill it with real data.
Example of filling the array for the first time with the most recent High prices:
private double[] recentHighs = new double[5]; // 5 slots to hold highs
protected override void OnBarUpdate()
{
if (CurrentBar < recentHighs.Length)
{
// Still filling the array for the first time
recentHighs[CurrentBar] = High[0];
}
}
✅ What’s happening here:
- On the very first bar,
CurrentBar == 0
, we setrecentHighs[0] = High[0]
- On the second bar,
CurrentBar == 1
, we setrecentHighs[1] = High[0]
- And so on, until all 5 slots are filled
This way, you build your history before doing anything complicated.
🔄 Step 2: Updating the Array as New Bars Come In
After you’ve filled the array once, you now want to keep it updated with the most recent values.
Since arrays are fixed size, you need to:
- Shift the old values backward
- Insert the newest value at the front
Example of shifting and updating:
protected override void OnBarUpdate()
{
// First, fill the array until full
if (CurrentBar < recentHighs.Length)
{
recentHighs[CurrentBar] = High[0];
return;
}
// After it's full, shift all values back by one slot
for (int i = recentHighs.Length - 1; i > 0; i--)
{
recentHighs[i] = recentHighs[i - 1];
}
// Insert the newest high at the front (slot 0)
recentHighs[0] = High[0];
}
✅ What’s happening now:
- Slot 4 becomes slot 3
- Slot 3 becomes slot 2
- Slot 2 becomes slot 1
- Slot 1 becomes slot 0
- Then the newest High is put in slot 0
This keeps the array always holding the last 5 Highs, newest first!
✏️ How to Change a Specific Value in an Array
At any time, you can manually update a value by specifying the index:
recentHighs[2] = Close[0]; // Replace the third-highest slot with today's Close
📈 Example: Finding the Highest Value in the Array
Let’s say you want to find the highest value in the array:
double maxHigh = recentHighs[0];
for (int i = 1; i < recentHighs.Length; i++)
{
if (recentHighs[i] > maxHigh)
maxHigh = Math.Max(maxHigh, recentHighs[i]);
}
// Now maxHigh holds the highest value from the recentHighs array
✅ This loop checks each slot one-by-one, keeping track of the biggest number it finds.
⚠️ Common Mistakes Beginners Make
Mistake | What Happens | How to Fix It |
---|---|---|
❌ Accessing a wrong index (like recentHighs[5] when you only have [0] to [4]) | Runtime Error: “Index was outside the bounds of the array” | Always stay inside 0 to Length - 1 |
❌ Forgetting to fill the array before using it | Wrong calculations or using 0.0 default values | Use CurrentBar to control when to fill vs update |
❌ Not shifting before inserting | New values overwrite old ones incorrectly | Always shift array elements before adding the new value |
❌ Never clearing old data when needed | Bad or outdated data stays inside | Use Array.Clear(recentHighs, 0, recentHighs.Length); to reset |
🧹 Clearing an Array (When You Need to Start Over)
Sometimes you want to wipe all the values clean — for example, at the start of a new session.
You can reset the whole array back to default (0.0 for doubles) like this:
Array.Clear(recentHighs, 0, recentHighs.Length);
✅ All the slots from [0]
to [4]
will be wiped clean and ready for new data!
🚀 Quick Summary of Arrays
- Arrays are fixed-size, indexed containers for values
- You must fill them first before you can shift or update them
- You must manually manage the shifting of old values
- Arrays are fast and memory-efficient — perfect when you know exactly how much data you need to track
- Always be careful about indexes and current bar logic
✅ Now you really understand what arrays are, how they work, and how to manage them properly in NinjaScript!
📋 Lists: Flexible and Dynamic
🧱 What Is a List?
A List is very similar to an array — it holds multiple values of the same type.
BUT:
- Unlike arrays, Lists can grow and shrink whenever you want! 🚀
- You don’t have to know how many values you need in advance.
Think of a List as a flexible container 🛍️ — you can keep adding new things, removing old things, and changing whatever you want easily.
🛠️ How to Create a List
In NinjaScript (and C#), here’s how you create a List that holds doubles:
List<double> recentHighs = new List<double>();
✅ Key Points:
List<double>
→ The list will hold doubles (decimal numbers)recentHighs
→ This is the name you gave your listnew List<double>()
→ Creates an empty list that you can fill later
No size is needed! It starts empty and grows when you add things.
✍️ How to Add Items to a List
You use .Add()
to insert a new item at the end of the List:
recentHighs.Add(High[0]);
✅ What this does:
- Adds the current bar’s High price to the end of the list.
- You can keep calling
.Add()
— it grows automatically!
🔄 Managing the Size of a List
Because Lists grow forever unless you manage them, you often want to limit how big they get.
For example, if you only want to keep the last 5 highs, you can remove the oldest value after adding a new one:
recentHighs.Add(High[0]); // Add newest value
if (recentHighs.Count > 5) // More than 5 items?
{
recentHighs.RemoveAt(0); // Remove the oldest (first) item
}
✅ What’s happening:
- Newest high goes at the end.
- If the list has grown too big, remove the very first item (oldest value).
This way, your List always contains only the latest 5 highs, automatically!
🖊️ How to Update a Value in a List
You can change any value in a List by indexing it (just like an array):
recentHighs[2] = Close[0]; // Change the third-highest value to the current Close
✅ Indexes work the same way:
- First item is
[0]
- Second item is
[1]
, etc.
📈 Example: Finding the Highest Value in a List
You can loop through a List exactly like an array:
double maxHigh = recentHighs[0];
for (int i = 1; i < recentHighs.Count; i++)
{
if (recentHighs[i] > maxHigh)
maxHigh = Math.Max(maxHigh, recentHighs[i]);
}
✅ This will give you the highest number in the List, no matter how many items are in it.
🧹 How to Clear a List
If you want to wipe the entire list (start over fresh):
recentHighs.Clear();
✅ After .Clear()
, the List will have zero items inside, ready to refill.
⚠️ Common Mistakes Beginners Make
Mistake | What Happens | How to Fix It |
---|---|---|
❌ Forgetting to limit the List size | The List grows forever and uses a lot of memory | Add if (recentHighs.Count > X) and remove old values |
❌ Indexing the wrong position | Runtime Error: “Argument out of range” | Always check Count before accessing |
❌ Forgetting .Add() | List stays empty — no data to work with | Make sure .Add() is called inside OnBarUpdate() |
❌ Mixing up indexes | Wrong values used for comparisons | Remember: [0] = first (oldest unless you manage insertion order) |
🤔 When Should You Use a List Instead of an Array?
Use a List When… | Use an Array When… |
---|---|
You don’t know exactly how many items you’ll have | You know exactly how many slots you need |
You want to dynamically add/remove items | You want ultra-fast fixed-size storage |
You want simple, automatic growing without shifting | You’re okay manually managing updates and shifts |
🧠 Important List Functions to Remember
Function | What It Does |
---|---|
.Add(item) | Adds a new item to the end |
.RemoveAt(index) | Removes the item at a specific position |
.Count | Tells you how many items are in the list |
.Clear() | Wipes the list back to empty |
🚀 Quick Summary of Lists
- Lists are dynamic — they grow and shrink automatically
- Easier to manage than arrays for changing amounts of data
- You must control the size if you only want to track a certain number of recent values
- Lists are slightly slower than arrays, but much more flexible for 99% of NinjaScript uses
✅ Now you know what Lists are, how to create them, how to update them, and how to manage their size properly in NinjaScript!
🗂️ Dictionaries: Keyed Data Tracking
🧱 What Is a Dictionary?
A Dictionary is a special kind of container that holds pairs of values:
- A key (like a label)
- A value (the data you want to store)
Think of it like a filing cabinet 🗄️:
- Each folder has a name (the key)
- Inside each folder is a piece of data (the value)
💡 Unlike Arrays and Lists — which use numbered indexes like [0]
, [1]
, etc. —
Dictionaries use meaningful labels that you define (like "Monday"
or Time[0]
).
🛠️ How to Create a Dictionary
Here’s how to create a Dictionary that tracks decimal values (double
) using strings as the keys:
Dictionary<string, double> dayHighs = new Dictionary<string, double>();
✅ What this means:
- The key is a
string
(like “Monday” or “RTH”) - The value is a
double
(price, level, range, etc.) - You can now add, look up, or remove data based on any key you choose
✍️ How to Add or Update Values
To add or update values in a Dictionary, you use the key like this:
dayHighs["Monday"] = High[0]; // Add or update the high for Monday
✅ If “Monday” doesn’t exist yet — it’s added
✅ If it does exist — the value is updated
You can also use variables as keys:
string sessionName = Bars.Session.Name;
dayHighs[sessionName] = High[0];
This lets you track values per session, per bar, or per custom label.
🔎 How to Check If a Key Exists
Before accessing a value, it’s good practice to check if the key already exists:
if (dayHighs.ContainsKey("Monday"))
{
double high = dayHighs["Monday"];
}
⚠️ Without this check, trying to access a missing key will cause an error.
🔄 Common Use Case: Store Values Per Day
Let’s say you want to track the total range for each trading day:
string dateKey = Time[0].Date.ToString();
if (!dayHighs.ContainsKey(dateKey))
dayHighs[dateKey] = High[0] - Low[0];
else
dayHighs[dateKey] += High[0] - Low[0]; // add to previous value
✅ This stores the daily range using the date as a key
✅ On each bar, if the day already exists, it adds to the total
📉 Removing a Value
If you need to delete something from a Dictionary:
dayHighs.Remove("Monday");
This removes the “Monday” entry completely.
🧹 Clearing the Whole Dictionary
Just like Lists, you can reset everything:
dayHighs.Clear();
Useful if you want to start over at the beginning of a new week, session, or strategy cycle.
🧠 Practical Key Types You Can Use
Key Type | Example | Use Case |
---|---|---|
string | "PreMarket", "LondonOpen" | Custom session names |
DateTime | Time[0].Date | Daily or hourly tracking |
int | CurrentBar or BarsSinceEntryExecution() | Bar-by-bar tracking |
double | Close[0] | Uncommon, but possible |
⚠️ Common Mistakes Beginners Make
Mistake | What Happens | How to Fix It |
---|---|---|
❌ Accessing a key that doesn’t exist | Error: “Key not found” | Use .ContainsKey() before reading |
❌ Using floating-point values (like Time[0]) directly | Keys mismatch due to minor differences | Use .Date or .ToString("HH:mm") to clean it |
❌ Letting the dictionary grow forever | High memory usage | Clear it periodically, or use logic to prune it |
❌ Adding the same key twice unnecessarily | Overwrites silently | Use .ContainsKey() to control this |
🔍 Example: Per-Session Tracking Dictionary
Want to track the highest Close for each session?
string sessionKey = Bars.Session.Name;
if (!dayHighs.ContainsKey(sessionKey))
dayHighs[sessionKey] = Close[0];
else if (Close[0] > dayHighs[sessionKey])
dayHighs[sessionKey] = Close[0];
✅ Stores or updates the highest Close seen during that session
🧠 Key Dictionary Methods and Properties
Function | What It Does |
---|---|
.Add(key, value) | Adds a new key-value pair (error if key exists) |
[key] = value | Adds or updates a key’s value |
.ContainsKey(key) | Checks if a key already exists |
.Remove(key) | Deletes a key and its value |
.Clear() | Empties the whole dictionary |
.Count | Tells you how many items are in it |
🚀 Quick Summary of Dictionaries
- Dictionaries store data as key-value pairs
- Keys let you label and organize your values logically
- Ideal for tracking daily values, per-session data, custom calculations, and more
- You must manage key existence and prevent uncontrolled growth
✅ Now you know how to use Dictionaries to build smarter, more flexible indicators and strategies in NinjaScript!
🧪 Real-World Examples: Arrays, Lists, and Dictionaries in Action
Now that you understand how to use these data structures, let’s look at practical ways to apply them in NinjaScript.
Each of the following examples includes:
- 🎯 A use case (why it’s useful in trading)
- 🛠️ The structure used (array, list, or dictionary)
- ✅ The core logic to make it work
📊 Example 1: Custom Volatility Tracker (Array)
🎯 Goal: Track the range of the last 5 bars and calculate the average range
🛠️ Uses: double[]
array
private double[] recentRanges = new double[5];
protected override void OnBarUpdate()
{
if (CurrentBar < recentRanges.Length)
{
recentRanges[CurrentBar] = High[0] - Low[0];
return;
}
for (int i = recentRanges.Length - 1; i > 0; i--)
recentRanges[i] = recentRanges[i - 1];
recentRanges[0] = High[0] - Low[0];
double avgRange = 0;
for (int i = 0; i < recentRanges.Length; i++)
avgRange += recentRanges[i];
avgRange /= recentRanges.Length;
Print("Average 5-bar range: " + avgRange);
}
✅ Good for quick bar-by-bar calcs like ATR alternatives
🪜 Example 2: Dynamic Pullback Tracking (List)
🎯 Goal: Track custom pullbacks and reset when trend breaks
🛠️ Uses: List<double>
private List<double> pullbackLows = new List<double>();
protected override void OnBarUpdate()
{
// Add newest low
pullbackLows.Add(Low[0]);
// Cap the list to the last 10 bars
if (pullbackLows.Count > 10)
pullbackLows.RemoveAt(0);
// Detect a trend shift (simple rule)
if (Close[0] > Highs[1])
{
pullbackLows.Clear(); // reset if new breakout
}
// Optional: check for lowest low in the pullback
double minLow = pullbackLows.Min();
Draw.Dot(this, "PullbackLow" + CurrentBar, false, 0, minLow, Brushes.Red);
}
✅ Excellent for flexible, event-driven state tracking
📅 Example 3: Session Range Memory (Dictionary)
🎯 Goal: Store and recall total session range per day
🛠️ Uses: Dictionary<string, double>
private Dictionary<string, double> sessionRanges = new Dictionary<string, double>();
protected override void OnBarUpdate()
{
string sessionKey = Time[0].Date.ToShortDateString();
double currentRange = High[0] - Low[0];
if (!sessionRanges.ContainsKey(sessionKey))
sessionRanges[sessionKey] = currentRange;
else
sessionRanges[sessionKey] += currentRange;
Print("Today's session range so far: " + sessionRanges[sessionKey]);
}
✅ Great for daily stats, signal filtering, and volume/range memory
🔁 Example 4: Reversal Tagging System (Array + Dictionary)
🎯 Goal: Track previous reversal prices and assign them custom names
🛠️ Uses: double[]
for recent reversals, Dictionary<string, double>
for tagging them
private double[] reversalLevels = new double[3];
private Dictionary<string, double> tagMap = new Dictionary<string, double>();
protected override void OnBarUpdate()
{
if (/* custom reversal condition */ Close[0] > Open[0] && Close[0] > Close[1])
{
// Shift reversal levels
for (int i = reversalLevels.Length - 1; i > 0; i--)
reversalLevels[i] = reversalLevels[i - 1];
reversalLevels[0] = High[0];
// Label each level with a tag
tagMap["Rev1"] = reversalLevels[0];
tagMap["Rev2"] = reversalLevels[1];
tagMap["Rev3"] = reversalLevels[2];
foreach (var tag in tagMap)
Draw.Text(this, tag.Key + CurrentBar, tag.Key + ": " + tag.Value.ToString("0.00"), 0, tag.Value, Brushes.Gold);
}
}
✅ Combines fast updating (array) with easy labeling (dictionary)
🔄 Example 5: Rolling Bias Score (List)
🎯 Goal: Create a bias score based on conditions met across the last 20 bars
🛠️ Uses: List<int>
private List<int> biasScore = new List<int>();
protected override void OnBarUpdate()
{
int score = 0;
if (Close[0] > Open[0]) score += 1;
if (Close[0] > SMA(Close, 10)[0]) score += 1;
if (RSI(Close, 14, 3)[0] > 50) score += 1;
biasScore.Add(score);
if (biasScore.Count > 20)
biasScore.RemoveAt(0);
double avgBias = biasScore.Average();
if (avgBias >= 2.5)
Draw.ArrowUp(this, "BiasUp" + CurrentBar, true, 0, Low[0] - 1 * TickSize, Brushes.Green);
}
✅ Makes it easy to track aggregated logic across recent bars
🧠 Final Thoughts
Working with Arrays, Lists, and Dictionaries in NinjaScript might feel like “just programming stuff” at first — but once you start using them in your indicators and strategies, you’ll realize they are the backbone of creative logic and custom signal building.
✅ Arrays give you raw speed and control — great for fixed-size, bar-by-bar analysis.
✅ Lists help you build flexible, evolving conditions — perfect for tracking recent setups or state transitions.
✅ Dictionaries let you track and label complex data across time, sessions, or any condition you define — opening the door to more intelligent logic.
💡 Think of these tools like building blocks: You don’t need all of them in every script. But if you know when and why to use each one, you unlock a new level of clarity and precision in your trading logic.
📚 Keep practicing by:
- Rebuilding one of your existing indicators using a List or Dictionary
- Adding session-based logic to an array system
- Tracking your trades or setups using labeled keys
You don’t need to become a master overnight. Just start applying one new idea at a time — and you’ll get better, faster, and more confident with each new build. 🚀
🎉 Prop Trading Discounts
💥91% off at Bulenox.com with the code MDT91