Hi! I faced the same issue where the Upstox sandbox just returns a basic success response and doesn’t track positions, holdings, or funds. So using it directly for real paper trading simulation was not effective. But I came up with a workaround that lets me simulate a full live strategy while still validating order API calls.
My Workaround Process
What I did was treat the app as if it’s running entirely live, but only route the order placement API through sandbox. This way, I still get to test my request formats and order flow without risking capital, while using live LTP data for accurate simulation.
For example, after placing an order via the sandbox, I immediately call the LTP API from live to fetch the price and use it as the entry. I use this same logic for partial exits or trailing SL — fetch LTP again, update the position object, and continue. This gives me a very close-to-reality paper trading system without relying on any fake sandbox fills.
Class Design
I have a UpstoxHelper class that wraps all Upstox API calls. Each strategy instantiates its own helper and passes a SANDBOX_MODE flag. This makes each strategy completely independent — some can run in live mode, others in sandbox — even simultaneously.
Here’s how I store the tokens at initialization:
if sandbox_mode:
logger.notice("SANDBOX MODE IS ACTIVE. Setting sandbox_mode_live_token")
self.sandbox_mode_live_token = access_token
self.sandbox_mode = True
else:
logger.notice("CAUTION!!! LIVE MODE. Setting LIVE TOKEN FOR USE")
self.access_token = access_token
self.sandbox_mode = False
Tokens are stored once: The SANDBOX_TOKEN is loaded earlier in the file from .env. That one’s stable and doesn’t change often (usually once a month), making it easy to use for multiple test runs without constantly refreshing. And live from wherever I set it every day.
Configuration Switcher
The key insight I had was: even if you switch tokens, if the configuration is stuck in sandbox mode (sandbox=True), it’ll keep using the sandbox environment. I dug into the SDK and noticed that the main difference is just two endpoint URLs — so I manually set them in the config object to be safe.
Every time an API is called, my helper uses a shared method: get_upstox_configuration(sandbox_mode, sandbox_available). This method returns a fresh config object depending on the use case.
Example:
# For LTP, sandbox isn't supported, so force live config
configuration = self.get_upstox_configuration(self.sandbox_mode, sandbox_available=False)
With this setup, I can run multiple strategies in parallel — some live, some sandbox — and each has full control over its config and execution path.
This setup ensures:
-
Orders get routed through sandbox only when I want to test structure, not real execution.
-
LTP and market data always come from live, so strategy simulation is accurate.
-
Each strategy can independently toggle sandbox or live, thanks to the SANDBOX_MODE flag at init time.
Since this involves working around some SDK design limitations (like how it sticks in sandbox mode), I’d recommend not sharing the full implementation publicly.
That said, this method has let me simulate and test real trading flows without risking real funds — so happy to share more details privately if anyone’s stuck.