Data Types
Data Types
Section titled “Data Types”Data types define what kind of information flows through your edges. Think of them like different pipes—you can’t pump oil through a water pipe!
Why Data Types Matter
Section titled “Why Data Types Matter”When you connect nodes, GrailHub checks if the data types match:
- ✅ Number output → Number input: Works perfectly!
- ❌ Number output → Text input: Won’t connect!
- ✅ Text output → Dynamic input: Works (dynamic accepts anything)
This prevents silly mistakes like sending “hello” to a node expecting a price.
Basic Types (The Simple Ones)
Section titled “Basic Types (The Simple Ones)”Number
Section titled “Number”Any numerical value—integers or decimals.
Examples:
150.75(stock price)1000(quantity)14.5(RSI indicator value)-5.2(profit/loss)
Used for: Prices, quantities, indicators, calculations
String (Text)
Section titled “String (Text)”Any text value wrapped in quotes.
Examples:
"AAPL"(stock symbol)"BUY"(order side)"Price is too high!"(alert message)
Used for: Symbols, messages, commands, labels
Boolean (True/False)
Section titled “Boolean (True/False)”Just two values: true or false.
Examples:
true(condition met!)false(not yet…)
Used for: Conditions, filters, decision points
Dynamic
Section titled “Dynamic”The flexible type—accepts anything! Use when you’re not sure what type you’ll get.
Examples:
- Can receive a number:
150.75 - Can receive text:
"AAPL" - Can receive complex data:
Tickerobject
Used for: Debug nodes, flexible processors, generic handlers
Complex Types (The Powerful Ones)
Section titled “Complex Types (The Powerful Ones)”These are specialized data structures for trading workflows.
Ticker
Section titled “Ticker”Real-time market price data.
What’s inside:
{
"symbol": "C_BTC_USDT",
"price": 95432.50,
"timestamp": 1703001234567,
"datasource": "binance"
}Used for: Live price monitoring, strategy triggers
Produced by: Market Ticker components
Consumed by: Indicators, conditions, calculators
AggregateBar (Candlestick)
Section titled “AggregateBar (Candlestick)”OHLCV candlestick data—the classic trading chart bar.
What’s inside:
{
"ticker": "AAPL",
"open": 150.00,
"high": 152.50,
"low": 149.75,
"close": 151.25,
"volume": 1500000,
"timestamp": 1703001234567
}Used for: Chart patterns, technical indicators, backtesting
Produced by: Historical data fetchers, aggregators
Consumed by: Pattern detectors (Doji, Hammer), RSI, moving averages
Series
Section titled “Series”Time series data—arrays of values with timestamps.
What’s inside:
{
"values": [150.0, 151.2, 149.8, 152.1],
"timestamps": [1703001000000, 1703002000000, 1703003000000, 1703004000000]
}Used for: Multi-period calculations, trend analysis
Produced by: Indicators (RSI, MA), aggregators
Consumed by: Chart displays, complex calculators
Upper and lower boundaries—like Bollinger Bands.
What’s inside:
{
"upper": {
"values": [152.0, 153.0, 154.0],
"timestamps": [1703001000000, 1703002000000, 1703003000000]
},
"lower": {
"values": [148.0, 147.5, 147.0],
"timestamps": [1703001000000, 1703002000000, 1703003000000]
}
}Used for: Bollinger Bands, support/resistance zones
Produced by: Band indicators
Consumed by: Strategy conditions, chart displays
Result of a trading order execution.
What’s inside:
{
"order_id": 123456789,
"symbol": "BTCUSDT",
"side": "BUY",
"type": "MARKET",
"quantity": 0.05,
"price": 95432.50,
"status": "FILLED",
"timestamp": 1703001234567
}Used for: Order tracking, PnL calculation, portfolio management
Produced by: Order execution components
Consumed by: PnL calculators, order loggers, alerts
OrderBook
Section titled “OrderBook”Market depth—all buy and sell orders at different price levels.
What’s inside:
{
"symbol": "BTCUSDT",
"bids": [
{"price": 95400.0, "quantity": 1.5},
{"price": 95390.0, "quantity": 2.3}
],
"asks": [
{"price": 95410.0, "quantity": 0.8},
{"price": 95420.0, "quantity": 1.2}
],
"timestamp": 1703001234567
}Used for: Market depth analysis, liquidity checks, spread calculations
Produced by: OrderBook stream components
Consumed by: Spread analyzers, liquidity filters
Message
Section titled “Message”Generic message from any chat platform (Telegram, Discord, Slack).
What’s inside:
{
"platform": "telegram",
"type": "command",
"content": "/buy",
"args": "AAPL 100",
"channel_id": "123456",
"user_id": "789012",
"metadata": {}
}Used for: Bot commands, chat integrations, user interactions
Produced by: Telegram/Discord listeners
Consumed by: Command parsers, chat responders
TimeFrame
Section titled “TimeFrame”Time period specification for historical data.
What’s inside:
{
"unit": "minute",
"amount": 5
}Valid units: minute, hour, day, week, month, year
Examples:
- 5 minutes:
{unit: "minute", amount: 5} - 1 hour:
{unit: "hour", amount: 1} - 1 day:
{unit: "day", amount: 1}
Used for: Historical data fetching, indicator calculations
Configured in: Pattern detectors, RSI components, data fetchers
How to Check Data Types
Section titled “How to Check Data Types”Look at the Dots
Section titled “Look at the Dots”Hover over any input or output dot—a tooltip shows the type!
- 🔵 Blue dot: Number
- 🟢 Green dot: String
- 🟣 Purple dot: Boolean
- ⚪ White dot: Dynamic (accepts any)
- 🟠 Orange dot: Complex type (hover to see which)
Connection Rules
Section titled “Connection Rules”- Exact Match: Number → Number ✅
- Dynamic Target: Anything → Dynamic ✅
- Type Mismatch: Number → String ❌
- Transform Available: Different types can connect if you add a transform expression
Type Transformation
Section titled “Type Transformation”Can’t connect two different types? Use edge transformations!
Example: Convert number to string
- Output:
150.75(Number) - Transform:
String(value) - Input receives:
"150.75"(String)
Example: Extract price from Ticker
- Output:
Tickerobject - Transform:
value.price - Input receives:
95432.50(Number)
Example: Format text
- Output:
150.75(Number) - Transform:
"Price: $" + String(value) - Input receives:
"Price: $150.75"(String)
Common Type Errors
Section titled “Common Type Errors””Cannot connect: incompatible types”
Section titled “”Cannot connect: incompatible types””Problem: You’re trying to connect Number to String input.
Solution:
- Check if you’re connecting the right outputs/inputs
- Use a transform expression to convert types
- Find a node that accepts Dynamic type
”Type mismatch in expression”
Section titled “”Type mismatch in expression””Problem: Your transform expression expects wrong type.
Solution:
- If input is Number: Use math operators (
value * 2) - If input is String: Use string functions (
value.toUpperCase()) - If input is Object: Use dot notation (
value.price)
“Expected Number, got String”
Section titled ““Expected Number, got String””Problem: Component received "150" but needs 150.
Solution: Add transform: Number(value) or parseFloat(value)
Pro Tips
Section titled “Pro Tips”Use Dynamic for Debugging
Section titled “Use Dynamic for Debugging”Add a Printer node with Dynamic input—it accepts and logs anything!
Check Component Documentation
Section titled “Check Component Documentation”Click any component to see:
- What types its inputs accept
- What types its outputs produce
- Example data structures
Type-Safe Chains
Section titled “Type-Safe Chains”Build flows left-to-right:
- Start with data source (Ticker, Bar)
- Process with type-compatible nodes
- End with action (Order, Alert)
Name Your Complex Types
Section titled “Name Your Complex Types”In flow settings, rename inputs/outputs to show their purpose:
ticker_datainstead ofinput1rsi_valueinstead ofoutput1
Common Questions
Section titled “Common Questions”Q: Can I convert any type to any other type?
A: You can try with transforms, but some conversions don’t make sense (like converting a Ticker object to a Boolean).
Q: What happens if I send the wrong type?
A: The node receiving it will turn red and show an error. Check the logs!
Q: Why can’t I see my custom type in the list?
A: Complex types like Ticker, Order, etc. are internal—you don’t create them directly. Components produce them.
Q: Is Dynamic type slower?
A: Slightly, but the difference is negligible for most flows.
Q: Can I create my own complex types?
A: Not in the UI—custom types are defined in component code. But you can use JavaScript objects with Dynamic type!