FX
FXSOCKET
  • Features
  • How it Works
  • Use Cases
  • Pricing
  • Private Hosting
  • Blog
Sign InGet API Key
Use Case

Historical Forex Data API for Backtesting

Access years of candlestick data from MT4 and MT5 through a single API call: OHLC bars with a per-bar spread, on every standard timeframe. Feed it into your backtesting engine: pandas, backtrader, Zipline, or anything custom. No screen scraping, no CSV downloads, no MQL scripts.

What Data Is Available

  • OHLC candles. M1, M5, M15, M30, H1, H4, D1, W1, MN1: all standard MetaTrader timeframes. Request any date range for any symbol your broker supports.
  • Per-bar spread. Every PriceHistory bar carries a spread column (in points). Drop down to M1 for spread analysis without a separate tick feed.
  • Real-time streams. The same WebSocket that powers live trading streams live ticks for paper trading and forward-testing.
  • Clean JSON responses. Every endpoint returns structured JSON. No parsing HTML, no binary formats, no proprietary protocols.

Example: Fetch Daily Candles

import requests
import pandas as pd

API_KEY = "fxs_live_..."
ACCOUNT_ID = "your_account_id"
BASE = "https://api.fxsocket.com/mt5"

response = requests.get(
    f"{BASE}/{ACCOUNT_ID}/PriceHistory",
    headers={"X-API-Key": API_KEY},
    params={
        "symbol": "EURUSD",
        "timeframe": "D1",
        "from": "2024-01-01",
        "to": "2024-12-31",
    },
)

# PriceHistory returns a JSON array of bars
# (time, open, high, low, close, tickVolume, realVolume, spread).
df = pd.DataFrame(response.json())
print(df.head())

Example: Complete Moving Average Crossover Backtest

The snippet above fetches data. Here's what a full backtest looks like: fetching H1 candles, computing a fast/slow SMA crossover, generating signals, and calculating returns, all in one script.

import requests
import pandas as pd
import numpy as np

API_KEY = "fxs_live_..."
ACCOUNT_ID = "your_account_id"
BASE = "https://api.fxsocket.com/mt5"

# Fetch one year of H1 candles
response = requests.get(
    f"{BASE}/{ACCOUNT_ID}/PriceHistory",
    headers={"X-API-Key": API_KEY},
    params={
        "symbol": "EURUSD",
        "timeframe": "H1",
        "from": "2024-01-01",
        "to": "2024-12-31",
    },
)

df = pd.DataFrame(response.json())
df["time"] = pd.to_datetime(df["time"])
df.set_index("time", inplace=True)

# Compute moving averages
df["sma_fast"] = df["close"].rolling(window=20).mean()
df["sma_slow"] = df["close"].rolling(window=50).mean()

# Generate signals: 1 = long, -1 = flat
df["signal"] = np.where(df["sma_fast"] > df["sma_slow"], 1, -1)

# Calculate returns
df["market_return"] = df["close"].pct_change()
df["strategy_return"] = df["signal"].shift(1) * df["market_return"]

# Results
cumulative = (1 + df["strategy_return"].dropna()).cumprod()
print(f"Final equity: {cumulative.iloc[-1]:.4f}")
print(f"Max drawdown: {(cumulative / cumulative.cummax() - 1).min():.4%}")
print(f"Total trades: {(df['signal'].diff() != 0).sum()}")

Example: Spread Analysis from M1 Bars

For strategies that depend on spread dynamics, every PriceHistory bar carries a spread column (in points), so you can study spread behaviour straight from M1 candles, no separate tick download needed. For live, sub-minute tick capture, subscribe to the WebSocket prices stream instead.

import requests
import pandas as pd

API_KEY = "fxs_live_..."
ACCOUNT_ID = "your_account_id"
BASE = "https://api.fxsocket.com/mt5"

# M1 bars for a specific session, each carries a per-bar spread.
response = requests.get(
    f"{BASE}/{ACCOUNT_ID}/PriceHistory",
    headers={"X-API-Key": API_KEY},
    params={
        "symbol": "EURUSD",
        "timeframe": "M1",
        "from": "2024-06-03T08:00:00",
        "to": "2024-06-03T16:00:00",
    },
)

df = pd.DataFrame(response.json())
df["time"] = pd.to_datetime(df["time"])

print(f"Total bars: {len(df)}")
print(f"Avg spread (points): {df['spread'].mean():.1f}")
print(f"Max spread (points): {df['spread'].max()}")

# Identify spread spikes (e.g., during news events)
spike_threshold = df["spread"].mean() + 3 * df["spread"].std()
spikes = df[df["spread"] > spike_threshold]
print(f"Spread spikes (>3σ): {len(spikes)}")

Fetching Data in Python, Node.js, and Go

There's no SDK to install. The data API is plain HTTP and JSON, so the same candle fetch works from any language. Pick whichever your backtesting pipeline runs in; the array you get back is identical.

Python

import requests

ACCOUNT_ID = "your_account_id"
resp = requests.get(
    f"https://api.fxsocket.com/mt5/{ACCOUNT_ID}/PriceHistory",
    headers={"X-API-Key": "fxs_live_..."},
    params={"symbol": "GBPUSD", "timeframe": "H4",
            "from": "2024-01-01", "to": "2024-06-30"},
)
bars = resp.json()

Node.js

const ACCOUNT_ID = "your_account_id";
const resp = await fetch(
  `https://api.fxsocket.com/mt5/${ACCOUNT_ID}/PriceHistory?` +
    new URLSearchParams({
      symbol: "GBPUSD",
      timeframe: "H4",
      from: "2024-01-01",
      to: "2024-06-30",
    }),
  { headers: { "X-API-Key": "fxs_live_..." } },
);
const bars = await resp.json();

Go

accountID := "your_account_id"
url := "https://api.fxsocket.com/mt5/" + accountID +
    "/PriceHistory?symbol=GBPUSD&timeframe=H4&from=2024-01-01&to=2024-06-30"

req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("X-API-Key", "fxs_live_...")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()

var bars []Bar
json.NewDecoder(resp.Body).Decode(&bars)

Prefer a typed client? Every account serves its own OpenAPI 3 spec at /mt5/{account_id}/api-doc/openapi.json. Point any OpenAPI generator at it to produce one for your language.

Integration with Popular Backtesting Frameworks

FxSocket returns clean JSON that converts directly into the data structures these frameworks expect. Here are quick examples for three popular Python backtesting libraries.

backtrader

import backtrader as bt
import requests
import pandas as pd
from io import StringIO

API_KEY = "fxs_live_..."
ACCOUNT_ID = "your_account_id"
BASE = "https://api.fxsocket.com/mt5"

resp = requests.get(
    f"{BASE}/{ACCOUNT_ID}/PriceHistory",
    headers={"X-API-Key": API_KEY},
    params={"symbol": "EURUSD", "timeframe": "D1",
            "from": "2023-01-01", "to": "2024-12-31"},
)

df = pd.DataFrame(resp.json())
df["time"] = pd.to_datetime(df["time"])
df.set_index("time", inplace=True)
df["volume"] = df["tickVolume"]
df = df[["open", "high", "low", "close", "volume"]]

data = bt.feeds.PandasData(dataname=df)

cerebro = bt.Cerebro()
cerebro.adddata(data)
cerebro.addstrategy(YourStrategy)
cerebro.run()

vectorbt

import vectorbt as vbt
import requests
import pandas as pd

API_KEY = "fxs_live_..."
ACCOUNT_ID = "your_account_id"
BASE = "https://api.fxsocket.com/mt5"

resp = requests.get(
    f"{BASE}/{ACCOUNT_ID}/PriceHistory",
    headers={"X-API-Key": API_KEY},
    params={"symbol": "EURUSD", "timeframe": "H1",
            "from": "2024-01-01", "to": "2024-12-31"},
)

df = pd.DataFrame(resp.json())
df["time"] = pd.to_datetime(df["time"])
close = df.set_index("time")["close"]

# Run a fast/slow MA crossover with vectorbt
fast_ma = vbt.MA.run(close, window=10)
slow_ma = vbt.MA.run(close, window=30)
entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)

portfolio = vbt.Portfolio.from_signals(close, entries, exits)
print(portfolio.stats())

Zipline

import pandas as pd
import requests

API_KEY = "fxs_live_..."
ACCOUNT_ID = "your_account_id"
BASE = "https://api.fxsocket.com/mt5"

# Fetch data and write to a Zipline-compatible CSV bundle
resp = requests.get(
    f"{BASE}/{ACCOUNT_ID}/PriceHistory",
    headers={"X-API-Key": API_KEY},
    params={"symbol": "EURUSD", "timeframe": "D1",
            "from": "2020-01-01", "to": "2024-12-31"},
)

df = pd.DataFrame(resp.json())
df["time"] = pd.to_datetime(df["time"])
df = df.rename(columns={"time": "date", "tickVolume": "volume"})
df = df[["date", "open", "high", "low", "close", "volume"]]

# Save as CSV for Zipline ingestion
df.to_csv("eurusd_daily.csv", index=False)

The pattern is always the same: call the API, convert to a DataFrame, and hand it to whatever framework you prefer. No format conversion, no adapter libraries, no custom data feed classes beyond what the framework itself provides.

Data Quality and Considerations

Historical data quality can make or break a backtest. Here are the things you need to know when working with FxSocket data.

Broker-Specific Data

FxSocket pulls data directly from your broker's MetaTrader server. This means prices, spreads, and available history are specific to your broker. Two brokers will have slightly different bid/ask prices for the same symbol at the same timestamp. This is not a bug; it's how the forex market works. Every broker's liquidity providers and markup are different. The advantage is that you're backtesting on the exact same data feed you'll trade on live, so your fills and slippage estimates are more realistic than with a generic data source.

Handling Gaps: Weekends and Holidays

The forex market closes Friday evening (New York time) and reopens Sunday evening. Between those hours, there are no ticks and no candles. Your backtest needs to account for this. If you request a date range that spans a weekend, the API returns data up to Friday's close and resumes from Sunday's open; there are no artificial fills in between. Similarly, some brokers reduce liquidity or close certain instruments on holidays (e.g., Christmas, New Year). Gaps will appear naturally in the data. When calculating indicators like moving averages, make sure your logic handles these gaps rather than treating them as zero-volume periods.

Timezone Handling

MT4 and MT5 servers each run on the timezone their broker configures, often UTC+2 or UTC+3 (to align daily candles with the New York close). FxSocket returns timestamps exactly as reported by the broker's server. This means a D1 candle's open and close times depend on your broker's timezone setting. If you're comparing data across multiple brokers or merging with external data sources (e.g., economic calendar events in UTC), convert timestamps explicitly. Pandas makes this straightforward:

df["time"] = pd.to_datetime(df["time"])
# Convert from broker time (e.g., UTC+2) to UTC
df["time"] = df["time"].dt.tz_localize("Etc/GMT-2").dt.tz_convert("UTC")

Candle Close Times: MT4 vs MT5

MT4 uses four-digit pricing by default and stores candle timestamps as the candle open time. MT5 also uses the open time but may include additional precision. The more important difference is that MT5 supports more granular timeframes natively and can store tick data with millisecond precision, while MT4 tick history may be more limited depending on the broker. If you're running the same backtest on both MT4 and MT5 data, be aware that minor discrepancies in candle boundaries can occur because of different server configurations and timestamp resolution.

From Backtest to Live: Same Code

The biggest friction in algo trading is the gap between backtesting and live execution. With FxSocket, both use the same API. Pull historical data for backtesting, then switch to live execution by calling the OrderSend endpoint. No rewrite, no adapter layer, no separate data provider.

Common Errors and Troubleshooting

When pulling large amounts of historical data, you may run into a few edge cases. Here's how to handle them.

Handling Large Date Ranges

PriceHistory returns the whole requested window in a single JSON array; there's no pagination cursor. For very large pulls (years of M1), fetch month-by-month and concatenate, so each response stays manageable and a hiccup only costs you one chunk.

from datetime import date
from dateutil.relativedelta import relativedelta

all_bars = []
month, end = date(2022, 1, 1), date(2024, 12, 31)

while month < end:
    nxt = month + relativedelta(months=1)
    resp = requests.get(
        f"{BASE}/{ACCOUNT_ID}/PriceHistory",
        headers={"X-API-Key": API_KEY},
        params={
            "symbol": "EURUSD",
            "timeframe": "M1",
            "from": month.isoformat(),
            "to": nxt.isoformat(),
        },
    )
    all_bars.extend(resp.json())
    month = nxt

df = pd.DataFrame(all_bars)
print(f"Total bars: {len(df)}")

Symbol Availability

Not every broker offers every symbol. If you request candles for a symbol your broker doesn't support, the API returns a clear error message. You can list all available symbols for your connected account by calling GET /mt5/{account_id}/symbols. Keep in mind that symbol names can vary between brokers; one broker might use EURUSD, another EURUSDm or EURUSD.r. Always check the symbol list before hardcoding names in your backtest scripts.

Timeframe Availability on Older Data

Some brokers only keep a limited history for smaller timeframes. You might get five years of D1 data but only six months of M1 data. This is a broker-side limitation, not an API one. If you need deep M1 history and your broker doesn't have it, consider fetching the smallest available timeframe and resampling. For example, resample M5 candles into custom intervals for rough estimates.

Large Pulls and Timeouts

There are no enforced rate limits; every request hits your own dedicated terminal, so heavy pulling only ever slows down your own account. The practical limit is how fast that terminal can fetch history from your broker, so very wide M1 ranges can take a few seconds. For bulk data pulls, prefer larger date ranges per request (or the monthly-chunk loop above) over many tiny requests.

Why Traders Use FxSocket for Backtesting

  • One API for everything. Historical data, live data, and trade execution all through the same endpoints and authentication.
  • Broker-accurate data.Data comes directly from your broker's MetaTrader server, the same prices you'll trade on live.
  • No rate limits on data requests. Download large date ranges without throttling. Perfect for bulk backtests.
  • Works with any analysis tool. JSON output drops into pandas, R, Excel, or any data pipeline.
  • Sub-30ms execution. When you move from backtest to live, orders execute in under 30 milliseconds with 99.99% uptime.
  • No MQL required. No need to write Expert Advisors or MQL scripts. Your Python, Node.js, Go, or Rust code talks directly to the API.
  • No VPS needed. Run your backtests on your local machine, a cloud VM, or a CI pipeline. The API is accessible from anywhere.

Supported Symbols

Any symbol available on your broker's MT4 or MT5 server. Forex pairs, indices, commodities, crypto CFDs, stocks: if your broker offers it on MetaTrader, you can pull data for it.

Pricing

FxSocket starts at €12 per account per month on the Starter plan, which includes full access to historical data endpoints, real-time WebSocket streams, and trade execution. Pro plans are available for teams and higher-volume use cases. Every plan includes access to all supported timeframes (M1 through MN1), live WebSocket streams, and unlimited data requests.

Frequently Asked Questions

How far back does historical data go?

It depends on your broker. FxSocket pulls data directly from your broker's MetaTrader server, so the available history matches what your broker stores. Most brokers keep several years of D1 and H1 data, often going back 10+ years for major forex pairs. Smaller timeframes like M1 and M5 typically have shorter history, anywhere from a few months to a few years. If deep history is critical for your strategy, check your broker's data retention before you start.

Is the data the same as what I'd see on the MT4/MT5 chart?

Yes. FxSocket reads directly from the same MetaTrader server that feeds your charts. The OHLCV values, timestamps, and tick prices are identical to what the MT4 or MT5 terminal displays. If you open the same symbol and timeframe in your MetaTrader client and compare it to the API response, the numbers will match. This is by design; it means your backtest results reflect the actual prices you would have traded on.

Can I get tick-level data?

There's no bulk historical-tick download. For tick-level detail you have two options: every M1 PriceHistory bar carries a per-bar spread column (good for spread studies on past data), and the live WebSocket prices stream (plus getQuote for the latest tick) covers real-time capture. For most backtests, M1 bars are the practical floor.

How do I handle weekends and gaps in the data?

The API returns no data for periods when the market is closed. Weekend gaps (Friday close to Sunday open) are the most common, but you'll also see gaps on holidays depending on your broker. Your backtesting code should handle these naturally; don't interpolate or fill gaps with synthetic data, as that will distort your results. When computing rolling indicators, use the actual available bars rather than assuming a fixed number of bars per day or week.

Can I use FxSocket data with live trading on a different platform?

Absolutely. While FxSocket offers its own trade execution endpoints, you can use the data API purely for backtesting and analysis without placing any trades through it. Many developers pull historical data from FxSocket, run their backtests locally, and then deploy their live strategy on a different execution platform. The data is yours to use however you like once you've fetched it.

Start BuildingView API Docs
FX
FXSOCKET

Cloud-hosted MT4 & MT5 terminals with REST APIs and real-time WebSocket streams. No VPS required.

Product
  • Features
  • Pricing
  • Private Hosting
  • Documentation
  • Python SDK
  • Status
Use Cases
  • Algo Trading
  • Copy Trading
  • Backtesting
  • Prop Firms
Company
  • Contact
  • Terms
  • Privacy
  • info@fxsocket.com
Connect
  • Telegram
  • WhatsApp

© 2026 Quantum Performance Labs LLC. All rights reserved.