Prerequisites
- An API key (contact us to get one)
- Python 3.8+ or Node.js 16+
- Basic understanding of trading concepts
What We’ll Build
A Moving Average Crossover indicator that:- Calculates 20-period and 50-period Simple Moving Averages
- Generates a BUY signal when the fast MA crosses above the slow MA
- Generates a SELL signal when the fast MA crosses below the slow MA
Step 1: Set Up Your Project
mkdir ma_crossover
cd ma_crossover
pip install requests pandas
mkdir ma_crossover
cd ma_crossover
npm init -y
npm install axios
Step 2: Create the Configuration
Create aconfig.py or config.js file:
API_KEY = "your_api_key_here"
BASE_URL = "https://api.innova-trading.com"
# Indicator settings
SYMBOL = "EURUSD"
TIMEFRAME = 60 # 1 hour
FAST_MA = 20
SLOW_MA = 50
module.exports = {
API_KEY: "your_api_key_here",
BASE_URL: "https://api.innova-trading.com",
SYMBOL: "EURUSD",
TIMEFRAME: 60,
FAST_MA: 20,
SLOW_MA: 50,
};
Step 3: Fetch Market Data
import requests
import pandas as pd
from config import API_KEY, BASE_URL, SYMBOL, TIMEFRAME
def get_bars(limit=100):
"""Fetch OHLC bars from the API."""
url = f"{BASE_URL}/api/external/bars/{SYMBOL}/{TIMEFRAME}"
headers = {"Authorization": f"Bearer {API_KEY}"}
response = requests.get(url, params={"limit": limit}, headers=headers)
response.raise_for_status()
data = response.json()
df = pd.DataFrame(data["bars"])
return df
# Test it
if __name__ == "__main__":
bars = get_bars(100)
print(f"Fetched {len(bars)} bars")
print(bars.tail())
const axios = require("axios");
const config = require("./config");
async function getBars(limit = 100) {
const url = `${config.BASE_URL}/api/external/bars/${config.SYMBOL}/${config.TIMEFRAME}`;
const response = await axios.get(url, {
params: { limit },
headers: { Authorization: `Bearer ${config.API_KEY}` },
});
return response.data.bars;
}
// Test it
(async () => {
const bars = await getBars(100);
console.log(`Fetched ${bars.length} bars`);
console.log(bars.slice(-5));
})();
python main.py
# or
node main.js
Step 4: Calculate Moving Averages
from config import FAST_MA, SLOW_MA
def calculate_sma(df, period, column="close"):
"""Calculate Simple Moving Average."""
return df[column].rolling(window=period).mean()
def add_moving_averages(df):
"""Add fast and slow MAs to dataframe."""
df["fast_ma"] = calculate_sma(df, FAST_MA)
df["slow_ma"] = calculate_sma(df, SLOW_MA)
return df
# Test it
if __name__ == "__main__":
bars = get_bars(100)
bars = add_moving_averages(bars)
print(bars[["time", "close", "fast_ma", "slow_ma"]].tail())
const config = require("./config");
function calculateSMA(bars, period, field = "close") {
const result = [];
for (let i = 0; i < bars.length; i++) {
if (i < period - 1) {
result.push(null);
} else {
const slice = bars.slice(i - period + 1, i + 1);
const sum = slice.reduce((acc, bar) => acc + bar[field], 0);
result.push(sum / period);
}
}
return result;
}
function addMovingAverages(bars) {
const fastMA = calculateSMA(bars, config.FAST_MA);
const slowMA = calculateSMA(bars, config.SLOW_MA);
return bars.map((bar, i) => ({
...bar,
fast_ma: fastMA[i],
slow_ma: slowMA[i],
}));
}
Step 5: Detect Crossovers
def detect_crossovers(df):
"""
Detect MA crossovers and generate signal points.
Returns list of signal points in API format.
"""
points = []
for i in range(1, len(df)):
prev = df.iloc[i - 1]
curr = df.iloc[i]
# Skip if MAs are not yet calculated
if pd.isna(prev["fast_ma"]) or pd.isna(curr["slow_ma"]):
continue
# Bullish crossover: fast MA crosses above slow MA
prev_diff = prev["fast_ma"] - prev["slow_ma"]
curr_diff = curr["fast_ma"] - curr["slow_ma"]
if prev_diff <= 0 and curr_diff > 0:
# BUY signal
points.append({
"time": int(curr["time"]),
"type": "low",
"price": float(curr["low"]) - 0.0005, # Slightly below candle
"label": "BUY",
"color": "#3b82f6",
"shape": "arrowUp",
"size": 2
})
elif prev_diff >= 0 and curr_diff < 0:
# SELL signal
points.append({
"time": int(curr["time"]),
"type": "high",
"price": float(curr["high"]) + 0.0005, # Slightly above candle
"label": "SELL",
"color": "#f97316",
"shape": "arrowDown",
"size": 2
})
return points
function detectCrossovers(bars) {
const points = [];
for (let i = 1; i < bars.length; i++) {
const prev = bars[i - 1];
const curr = bars[i];
// Skip if MAs are not yet calculated
if (prev.fast_ma === null || curr.slow_ma === null) {
continue;
}
// Bullish crossover: fast MA crosses above slow MA
const prevDiff = prev.fast_ma - prev.slow_ma;
const currDiff = curr.fast_ma - curr.slow_ma;
if (prevDiff <= 0 && currDiff > 0) {
// BUY signal
points.push({
time: curr.time,
type: "low",
price: curr.low - 0.0005,
label: "BUY",
color: "#3b82f6",
shape: "arrowUp",
size: 2,
});
} else if (prevDiff >= 0 && currDiff < 0) {
// SELL signal
points.push({
time: curr.time,
type: "high",
price: curr.high + 0.0005,
label: "SELL",
color: "#f97316",
shape: "arrowDown",
size: 2,
});
}
}
return points;
}
Step 6: Submit to InnovaTrading
def submit_indicator(points):
"""Submit indicator points to the API."""
if not points:
print("No signals to submit")
return None
url = f"{BASE_URL}/api/external/indicators/ma_crossover"
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
payload = {
"symbol": SYMBOL,
"timeframe": TIMEFRAME,
"indicator_name": f"MA Crossover ({FAST_MA}/{SLOW_MA})",
"version": "1.0",
"points": points,
"metadata": {
"fast_period": FAST_MA,
"slow_period": SLOW_MA,
"total_signals": len(points)
}
}
response = requests.post(url, json=payload, headers=headers)
response.raise_for_status()
return response.json()
async function submitIndicator(points) {
if (points.length === 0) {
console.log("No signals to submit");
return null;
}
const url = `${config.BASE_URL}/api/external/indicators/ma_crossover`;
const payload = {
symbol: config.SYMBOL,
timeframe: config.TIMEFRAME,
indicator_name: `MA Crossover (${config.FAST_MA}/${config.SLOW_MA})`,
version: "1.0",
points,
metadata: {
fast_period: config.FAST_MA,
slow_period: config.SLOW_MA,
total_signals: points.length,
},
};
const response = await axios.post(url, payload, {
headers: {
Authorization: `Bearer ${config.API_KEY}`,
"Content-Type": "application/json",
},
});
return response.data;
}
Step 7: Put It All Together
def main():
print("=" * 50)
print("MA Crossover Indicator")
print("=" * 50)
# 1. Fetch data
print("\n1. Fetching market data...")
bars = get_bars(500)
print(f" Fetched {len(bars)} bars")
# 2. Calculate MAs
print("\n2. Calculating moving averages...")
bars = add_moving_averages(bars)
# 3. Detect crossovers
print("\n3. Detecting crossovers...")
signals = detect_crossovers(bars)
print(f" Found {len(signals)} signals")
if signals:
print("\n Recent signals:")
for s in signals[-5:]:
print(f" - {s['label']} at {s['price']:.5f}")
# 4. Submit to API
print("\n4. Submitting to InnovaTrading...")
result = submit_indicator(signals)
if result:
print(f" Success! {result['points_received']} points submitted")
print(f" Expires at: {result['expires_at']}")
else:
print(" No signals to submit")
print("\n" + "=" * 50)
print("Done! Check your chart to see the signals.")
print("=" * 50)
if __name__ == "__main__":
main()
async function main() {
console.log("=".repeat(50));
console.log("MA Crossover Indicator");
console.log("=".repeat(50));
try {
// 1. Fetch data
console.log("\n1. Fetching market data...");
let bars = await getBars(500);
console.log(` Fetched ${bars.length} bars`);
// 2. Calculate MAs
console.log("\n2. Calculating moving averages...");
bars = addMovingAverages(bars);
// 3. Detect crossovers
console.log("\n3. Detecting crossovers...");
const signals = detectCrossovers(bars);
console.log(` Found ${signals.length} signals`);
if (signals.length > 0) {
console.log("\n Recent signals:");
signals.slice(-5).forEach((s) => {
console.log(` - ${s.label} at ${s.price.toFixed(5)}`);
});
}
// 4. Submit to API
console.log("\n4. Submitting to InnovaTrading...");
const result = await submitIndicator(signals);
if (result) {
console.log(` Success! ${result.points_received} points submitted`);
console.log(` Expires at: ${result.expires_at}`);
}
console.log("\n" + "=".repeat(50));
console.log("Done! Check your chart to see the signals.");
console.log("=".repeat(50));
} catch (error) {
console.error("Error:", error.message);
}
}
main();
Step 8: Run It!
python main.py
# or
node main.js
==================================================
MA Crossover Indicator
==================================================
1. Fetching market data...
Fetched 500 bars
2. Calculating moving averages...
3. Detecting crossovers...
Found 8 signals
Recent signals:
- BUY at 1.10250
- SELL at 1.10450
- BUY at 1.10300
4. Submitting to InnovaTrading...
Success! 8 points submitted
Expires at: 2025-12-13T14:00:00Z
==================================================
Done! Check your chart to see the signals.
==================================================
Step 9: View on Chart
- Open InnovaTrading
- Navigate to EURUSD H1 chart
- Open the External Indicators panel
- Your “MA Crossover” indicator should appear
- Toggle it ON to see the signals

Next Steps
Add Stop Loss & Take Profit
Make your signals actionable with risk management levels
Continuous Updates
Set up automatic signal updates