Friday's close does not stop Quotex. It changes the symbols.
The pairs your bot traded all week stop returning quotes, and a parallel set of instruments carrying an _otc suffix keeps printing candles straight through Saturday and Sunday. A loop written against weekday assets spends that whole window logging asset closed while the platform is quoting AUDCAD_otc at a full payout on the other side of the same socket.
Closing that gap is a small amount of code and a fairly large amount of judgment. What people search for as the Quotex OTC API is really just that delta, and this guide covers all of it: how OTC symbols are named and enumerated, how to tell when the window is genuinely open instead of guessing from a clock, the exact subscribe and order calls with an _otc symbol, one worked weekend loop that ties them together — and why a price series the broker generates itself should not inherit the risk and backtesting assumptions you built on a real market feed.
It assumes you already have an authenticated client. Getting credentials, handling the session and speaking the socket protocol are their own subject; everything below starts from a connected object.
Key Takeaways
There is no separate Quotex OTC API — the same connection, subscribe and order calls work, and only the symbol string, the availability flag and the price's origin change.
Resolve symbols at runtime from the open flag on the instrument row instead of hardcoding a weekend schedule; the window, the roster and the payout all move underneath a long-running loop.
Pass order parameters as keyword arguments — the positional order genuinely differs between the community Quotex libraries, so a ported snippet can send your stake where the symbol belongs.
Quotex generates OTC prices itself, so there is no outside print to reconcile against: treat an OTC backtest as evidence about that feed only, and forward-test a full weekend on a practice balance first.
Table of Contents (27 min read)Contents
What's Different About the OTC API vs the Regular Quotex API
Start with the fact that saves you a day of searching: there is no separate OTC API. No second endpoint, no different authentication, no OTC-only method set. You talk to the same unofficial broker API over the same WebSocket streaming connection and call the same subscribe and buy methods you already use.
Four things change, and every one of them is a property of the symbol, not of the interface:
The symbol string carries an _otc suffix.
The availability window is inverted — OTC instruments are live precisely when their weekday twins are shut.
The price origin is the platform itself rather than an aggregated market feed.
The payout and expiry roster is quoted per symbol and moves independently of the weekday version.
Regular market symbol
OTC symbol
Symbol passed to the API
EURUSD
EURUSD_otc
When it accepts orders
During the underlying market's session
Mainly outside it — weekends and off-hours
Where the price comes from
Derived from an outside market feed
Generated by the platform
Order book behind it
An external one exists to reconcile against
None you can see or verify
Calls you write
connect, subscribe, buy, check result
Identical — only the symbol differs
What a backtest on it proves
Something about that market
Something about that synthetic series only
Everything that changes for OTC is a property of the symbol you pass in — the interface stays the same.
The first two differences are mechanical and take an afternoon. The third is the one that quietly invalidates work: a rule tuned on an externally-derived EUR/USD series is not tuned on the OTC market instrument that shares its name. Build the code first, then come back to that.
How Are OTC Assets Identified in API Calls?
The convention is a lowercase _otc appended to the uppercase base symbol: EURUSD_otc, AUDCAD_otc, USDPKR_otc. That is the string the buy and subscribe calls expect, and it is the only place the OTC distinction lives in your code.
Two traps sit around it.
The display name is not the API name. The instrument list hands back pairs of [api_name, display_name], and the display side is formatted for a human — something like EUR/USD (OTC). Pass that into an order call and it is rejected as an unknown asset. Keep the two apart deliberately; this is ordinary symbol mapping discipline, and getting it wrong produces a failure that looks like an outage rather than a typo.
The roster is not fixed. Some OTC symbols have no weekday counterpart at all — exotic pairs and platform-only instruments that exist purely as synthetic series. Others appear and disappear between weekends. Deriving your OTC list by appending _otc to the pairs you already trade will silently miss the first group and silently break on the second. Enumerate instead:
pythonenumerate_otc.py
import asyncio
from pyquotex.stable_api import Quotex
async def live_otc_symbols(client):
"""Every symbol the platform is currently quoting as OTC."""
await client.get_instruments() # fills the instrument cache
names = client.get_all_asset_name() or [] # [[api_name, display_name], ...]
live = []
for api_name, display_name in names:
if not api_name.endswith("_otc"):
continue
_, status = await client.check_asset_open(api_name)
# status is (code, display_name, is_open)
if status[2]:
live.append((api_name, display_name))
return live
async def main():
client = Quotex(email="[email protected]", password="...")
ok, reason = await client.connect()
if not ok:
raise SystemExit(f"connect failed: {reason}")
for api_name, display_name in await live_otc_symbols(client):
print(f"{api_name:<16} {display_name}")
# Resolve one pair: fall back to its OTC twin when the weekday market is shut.
symbol, status = await client.get_available_asset("EURUSD", force_open=True)
print(symbol, "open" if status[2] else "closed") # -> EURUSD_otc open
await client.close()
asyncio.run(main())
get_available_asset(..., force_open=True) does the weekday-to-OTC swap for you; index 2 of the status tuple is the open flag.
That force_open argument is worth reading twice. It checks the asset you asked for, and if it is shut it flips the suffix — appending _otc to a plain symbol, or stripping it from an OTC one — and re-checks. One call therefore expresses the entire runtime rule your weekend bot needs.
Which symbol should the bot trade right now?
Take itProceed with careSkip / stand aside
The whole OTC branch is one question asked at runtime, not a calendar baked into your config.
The OTC Window Is a Flag, Not a Schedule
The weekly rhythm is easy to describe and dangerous to hardcode. Weekday sessions run on the regular instruments; at the Friday close those go dark; through the weekend only the OTC set keeps printing; when the market reopens the weekday symbols come back and the platform's OTC roster shifts again. That is the picture weekend OTC trading refers to.
Weekday sessions
Regular symbols quote and accept orders. Some OTC symbols run alongside them; do not assume OTC means weekend-only.
Friday close
Regular symbols stop returning quotes. A bot that resolved its symbol at startup begins failing here.
Weekend OTC window
The OTC roster is what is left. This is the stretch your loop has to survive without a weekday asset to fall back on.
Reopen
Weekday symbols return, some OTC symbols go quiet, and the payout profile you cached on Saturday is stale.
Your bot has to survive two transitions, not one — and both are announced by a flag on the instrument row, never by your local clock.
So resolve at runtime, and keep resolving. Four things make a hardcoded schedule wrong sooner than you expect:
The server's clock is not yours. Expiry alignment and window boundaries are the platform's, and a bot reasoning in local time drifts against them. Handle it with a real time-zone database rather than a fixed hour offset, and treat clock drift between your host and the server as a live failure mode, not a rounding error.
Holidays move the boundary. The weekday market can be shut on a Monday your calendar calls a trading day.
The roster changes. Symbols are added and retired; the one you traded last weekend may simply not be in the list.
Maintenance happens mid-window. An OTC symbol can flip closed on a Saturday afternoon with no announcement.
The counterweight is that re-polling costs requests. Reading the whole instrument list every second is a reliable way to meet API rate limiting — or worse, an account restriction on an unofficial client. Re-resolve on a slow cadence, once a minute is generous, and re-resolve immediately after any rejection rather than continuously in anticipation of one.
Pulling Live OTC Quotes and Candle Data
Two shapes cover everything a weekend bot needs, and neither changes for OTC beyond the symbol.
The historical pull asks for closed candles ending at a timestamp: you give it the asset, the end time, how many seconds of history you want, and the candle period. It returns a list you can feed straight into an indicator.
The live subscription is the one that surprises people. The subscribe call registers your interest and returns nothing useful — the socket then fills a buffer on the client object, and you read from that buffer separately. Three consequences follow: the buffer is empty for the first moment after subscribing, so your first read must tolerate nothing; the buffer's shape differs between forks, keyed by timestamp in some and a plain list in others; and a silent reconnect drops your subscription, so re-subscribing is part of your reconnect path, not an optional extra.
pythonotc_quotes.py
import asyncio
import time
PERIOD = 60 # candle size in seconds
async def history(client, symbol, period=PERIOD, bars=120):
"""Pull closed candles: (asset, end_from_time, offset_seconds, period)."""
candles = await client.get_candles(symbol, time.time(), bars * period, period)
return candles or []
async def stream(client, symbol, period=PERIOD):
"""Subscribe once, then read the shared buffer the socket fills."""
await client.start_candles_stream(symbol, period) # registers; returns nothing
try:
while True:
buf = await client.get_realtime_candles(symbol)
if not buf: # empty for the first second or two
await asyncio.sleep(0.5)
continue
# The buffer is keyed by candle timestamp on some forks and is a
# plain list on others - handle both instead of assuming.
key = max(buf) if isinstance(buf, dict) else -1
yield buf[key]
await asyncio.sleep(1)
finally:
await client.stop_candles_stream(symbol)
Subscribing does not return candles — it starts a stream that fills a buffer you poll. Always handle the empty first read.
One caution on history depth. Treat it as something you measure rather than assume: request the window you want, then check what actually came back before letting an indicator warm up on it. And keep parallel history requests modest — fanning out a deep backfill across many workers is the fastest way to get an unofficial session throttled or cut off entirely.
Placing an OTC Trade Programmatically
The order call is identical to the weekday one. You pass an amount in account currency, the symbol, a direction of call or put, and a duration in seconds. Swapping EURUSD for EURUSD_otc is the entire OTC change.
There is one gotcha that costs people real money, and it is not in any README: the parameter order is not consistent across the community libraries. One family signs the call as buy(amount, asset, direction, duration); older forks sign it as buy(asset, amount, direction, duration). Positional arguments therefore port between libraries silently and wrongly — your stake ends up where the symbol should be. Use keyword arguments every time, in every fork, without exception.
Two more details specific to running this unattended:
Check the payout before you commit the order. The payout percentage on an OTC symbol is quoted per symbol and per expiry, and it moves. Since a binary loss is bounded at the stake, that number is your break-even threshold — run yours through the break-even win rate calculator and set a floor below which the bot simply skips the symbol.
The result call blocks until expiry. Awaiting it inline stalls your stream for the full duration of the trade. Fire it as a background task and let the loop keep reading candles.
pythonplace_otc_trade.py
async def place_otc(client, symbol, amount, direction, duration):
"""direction: 'call' or 'put'. duration: expiry in seconds."""
payout = client.get_payout_by_asset(symbol, timeframe="1")
print("payout profile:", payout)
ok, info = await client.buy(
amount=amount, # ALWAYS keyword args - forks disagree on the order
asset=symbol,
direction=direction,
duration=duration,
)
if not ok:
return None, info # info carries the rejection reason
order_id = info.get("id")
outcome, profit = await client.check_win(order_id) # blocks until expiry
return order_id, (outcome, profit)
# Practice balance first. Swap to "REAL" only after a full weekend of forward testing.
await client.change_account("PRACTICE")
order_id, result = await place_otc(client, "AUDCAD_otc", 5, "put", 60)
print(order_id, result) # -> 12345678 ('win', 4.1)
Keyword arguments are not style here — positional order genuinely differs between Quotex libraries.
Do all of this on the practice balance first. A demo account costs you a weekend and tells you what your loop actually does at 3am on a Sunday when a symbol goes quiet mid-trade — which is information no amount of local testing produces.
A Minimal Weekend OTC Auto-Trade Loop
Two isolated calls are not the thing you actually need. This is: connect, resolve a tradable symbol, gate on payout, stream candles, evaluate a rule, place the order, settle it out of band, and go back to re-resolving because the window will move underneath you.
The entry rule below is deliberately a placeholder — three rising closes, no edge, there so the shape of the loop is visible. Replace it with your own logic and read nothing into it.
pythonweekend_otc_loop.py
import asyncio
import time
from pyquotex.stable_api import Quotex
BASE = "EURUSD"
PERIOD = 60
STAKE = 5
MIN_PAYOUT = 80 # skip the symbol if the payout drops below your floor
COOLDOWN = 120 # seconds between entries, whatever the signal says
async def resolve(client, base=BASE):
"""Return a tradable symbol, preferring the weekday asset, else its OTC twin."""
symbol, status = await client.get_available_asset(base, force_open=True)
return (symbol, status) if status and status[2] else (None, status)
def entry_signal(candles):
"""PLACEHOLDER. Replace with your own rule - this one has no edge."""
if len(candles) < 3:
return None
closes = [c["close"] for c in candles[-3:]]
if closes[0] < closes[1] < closes[2]:
return "call"
if closes[0] > closes[1] > closes[2]:
return "put"
return None
async def settle(client, order_id):
outcome, profit = await client.check_win(order_id)
print(f"order {order_id}: {outcome} {profit:+.2f}")
async def weekend_loop():
client = Quotex(email="[email protected]", password="...")
ok, reason = await client.connect()
if not ok:
raise SystemExit(f"connect failed: {reason}")
await client.change_account("PRACTICE")
symbol = None
last_entry = 0.0
try:
while True:
# Re-resolve on a slow cadence: the window and the roster both move.
symbol, status = await resolve(client)
if not symbol:
print("nothing tradable; sleeping")
await asyncio.sleep(60)
continue
payout = client.get_payout_by_asset(symbol, timeframe="1") or {}
if (payout.get(symbol, {}).get("profit", {}).get("1M") or 0) < MIN_PAYOUT:
await asyncio.sleep(60)
continue
await client.start_candles_stream(symbol, PERIOD)
candles = await client.get_candles(symbol, time.time(), 30 * PERIOD, PERIOD)
direction = entry_signal(candles or [])
if direction and time.time() - last_entry > COOLDOWN:
ok, info = await client.buy(
amount=STAKE, asset=symbol,
direction=direction, duration=PERIOD,
)
if ok:
last_entry = time.time()
# Settle in the background so the loop keeps streaming.
asyncio.create_task(settle(client, info.get("id")))
else:
print("rejected:", info)
await asyncio.sleep(PERIOD)
finally:
if symbol:
await client.stop_candles_stream(symbol)
await client.close()
asyncio.run(weekend_loop())
The whole OTC delta lives in resolve() and the payout gate; the rest is an ordinary trade loop.
Notice what the loop refuses to do. It never caches a symbol across iterations, it never trusts a payout it read an hour ago, it never lets settlement block the stream, and it enforces a cooldown independent of how often the signal fires. Each of those is a direct answer to a way weekend automation fails in production.
Why Synthetic OTC Pricing Changes Your Risk and Backtesting Assumptions
Here is the part a library README will never tell you, and it matters more than any of the code above.
An OTC price is generated, not aggregated. There is no exchange print behind it, no interbank quote it tracks, no external liquidity taking the other side. The platform authoring the series is also the counterparty to your trade — the ordinary structure of a market-maker broker, taken to its limit, because on a weekend there is no outside reference price to anchor it to at all. An OTC series is produced inside the platform that is also your counterparty — there is no outside print to check a move against.
For a bot, that has four concrete consequences.
You have no independent verification. On a weekday feed you can compare a move against another data source and see whether it happened. On an OTC series there is nothing outside the platform to compare against — the feed is its own only witness. Anomaly detection that assumes an outside reference has nothing to reference.
A backtest on OTC data is evidence about OTC data. It is not a weaker version of a weekday backtest; it is a different experiment. Weekday-tuned parameters do not transfer onto the synthetic series, and OTC-tuned parameters do not transfer back. Keep two books, and never quote a result from one as though it described the other.
Overfitting risk is higher, not lower. A generated series has no session structure, no news, no volume to explain a regime change — so when its behaviour shifts, nothing in your data tells you why. A rule fitted tightly to last month's OTC candles is overfitting to a series whose generating process can change without any observable cause. Short holds and small parameter counts age better here than they do on real feeds.
Your risk is bounded per trade and unbounded per weekend. A binary loss caps at the stake, which is genuinely reassuring for one position and completely misleading across 200 of them in 48 unattended hours. Size for the weekend, not the ticket: a per-session loss cap and a hard trade ceiling in the loop matter more than any entry rule you write.
Handling OTC-Specific Errors and Edge Cases
These are the failures that only appear on OTC symbols, in roughly the order you will meet them:
Unknown or inactive asset. You passed a symbol that is not in this weekend's roster, or one that closed since you resolved it. Re-resolve rather than retry the same string, and treat the order rejection message as the signal to do so.
The display name leaked into a call.EUR/USD (OTC) is not an asset identifier. If a rejection mentions an asset you are certain exists, check which of the two names your code passed.
An empty realtime buffer. Normal immediately after subscribing, and also what you see when a reconnect silently dropped the subscription. Distinguish them by elapsed time: empty for a second is startup, empty for a minute is a lost stream.
Stale candles. The buffer keeps returning the same timestamp while the clock advances. Compare the newest candle's timestamp against the server time and stop trading when the gap exceeds one period — a rule evaluated on a frozen candle will happily fire forever.
Session expiry mid-weekend. Sessions age out, and the reconnect that follows brings back a connected client with no subscriptions and, on some forks, the wrong balance mode. Re-assert the practice or real selection and re-subscribe after every reconnect.
Payout moved between the check and the order. The gate you passed a minute ago is not the payout you traded. Re-read it in the same iteration as the order, not at the top of the loop.
Expiry misalignment. Duration is counted from the server's clock. If your host's time is off, a 60-second expiry lands on a different candle boundary than the one your rule evaluated.
Log every one of these with the symbol and the resolved-at timestamp. Weekend failures are hard to reproduce on a Tuesday, and the symbol-resolution history is usually the only thing that explains them.
Cross-Checking Your Bot's Calls Against an Independent Signal Feed
The uncomfortable part of everything above is that your bot has no second opinion. It reads a feed the platform generates, evaluates it, and trades back into the same platform — a closed circuit with no outside witness at any point.
One practical way to open that circuit while you are still validating is to watch what an unrelated feed is calling on the same instruments and compare it against your own output. Our binary options signals page publishes live calls you can view for free and read alongside your bot's decisions — if your loop is firing constantly on a synthetic pair while an outside feed sees nothing worth acting on, that mismatch is worth investigating before you commit real capital to the strategy.
Be clear about what it is and is not: it is a human-readable feed, not an API endpoint or an execution service. It will not plug into your code, it does not replace the OTC quote stream you are building against, and it is a sanity check on your reasoning rather than a source of truth about a series only the platform can see.
Key Takeaways
The OTC delta is genuinely small in code and genuinely large in interpretation. You already have the connection; adding OTC means appending a suffix, asking the instrument row whether that symbol is open right now instead of consulting a calendar, and re-asking on every pass because the window and the roster both move. The subscribe and order calls do not change at all.
What does change is what your results mean. A weekend of clean fills on a platform-generated series tells you your plumbing works. It does not tell you the rule has an edge, and it says nothing about how the same rule behaves when the real market reopens on Monday. Build the loop, run it on a practice balance through an actual weekend, and keep its results in a book of their own — and if running and maintaining that loop is not what you signed up for, the ready-made Quotex trading bots solve a different version of the same problem.
No — there is no broker-published SDK for OTC or for anything else, so every method shown here belongs to a community library that reimplements the platform's own client protocol. Whether an official Quotex API exists at all is a separate question worth answering before you build on top of an unofficial one.
Can I trade OTC assets on weekdays too?
Often, yes. OTC is not strictly a weekend product on Quotex — part of the roster runs alongside the regular market during the week. This is precisely why the open flag beats a schedule: the same code path handles a midweek OTC symbol and a Sunday-morning one without a special case.
Why does my OTC candle subscription return an empty list?
Because subscribing registers interest rather than returning data — the socket fills a buffer afterwards, and your first read arrives before anything is in it. Poll with a short sleep until the buffer is populated. If it stays empty for longer than a minute, assume the subscription was dropped by a reconnect and re-subscribe.
Does a backtest on OTC data tell me anything about weekday performance?
Treat the answer as no. The two series are produced by completely different processes, so parameters fitted on one are not evidence about the other in either direction. If you want to know how a rule behaves on the live market, test it on live-market data.
Should I run the OTC loop on a demo account first?
Yes, and specifically for a full weekend rather than an hour. The failures that matter — a symbol closing mid-trade, a session expiring at 3am, a reconnect that quietly drops your stream — only show up across the whole unattended window, which is exactly the stretch you are automating.
Sources & Further Reading
Want to go deeper? These independent, authoritative sources shaped this guide — each one is worth reading in full:
The Binary Options Desk is the SignalBots editorial team for fixed-time and OTC trading coverage. We research and write the guides that explain expiry timing, payout structure and disciplined entry across the major brokers.
Discussions 0
Leave a comment