17 min readJust now
–
A technical breakdown of developing a trading platform with Gemini, Claude and ChatGPT. With real prompts, architectural struggles, and a few hard-won conclusions.
Hey everyone. My name is Artem, and for the last 6 months, I’ve been building a full-fledged algorithmic trading platform. About 95% of the code was generated by modern LLMs — mostly Gemini 2.5 Pro. My manual edits account for less than 5%.
Press enter or click to view image in full size
Depth Sight UI
The project is called Depth Sight. It’s a platform with a flexible visual strategy builder, backtesting, real/paper trading, a mobile PWA, and a natively integrated AI assistant to help create, explain, and analyze trading strategies. But this article isn’t really about algotrading.…
17 min readJust now
–
A technical breakdown of developing a trading platform with Gemini, Claude and ChatGPT. With real prompts, architectural struggles, and a few hard-won conclusions.
Hey everyone. My name is Artem, and for the last 6 months, I’ve been building a full-fledged algorithmic trading platform. About 95% of the code was generated by modern LLMs — mostly Gemini 2.5 Pro. My manual edits account for less than 5%.
Press enter or click to view image in full size
Depth Sight UI
The project is called Depth Sight. It’s a platform with a flexible visual strategy builder, backtesting, real/paper trading, a mobile PWA, and a natively integrated AI assistant to help create, explain, and analyze trading strategies. But this article isn’t really about algotrading. It’s about a new way to build complex software. It’s a case study of whether a person with a product vision, but without a team, can build an industrial-grade platform. Or not? Let’s figure it out together.
A critical disclaimer: I have no commercial development experience. No computer science degree. I’ve never worked on an IT team, maintained a production system, or dealt with enterprise workflows. My previous experience was limited to hobby projects (I tinkered with PHP and Python a long time ago) and one small crypto screener that became a prototype for this project — also written with LLMs. It was the Large Language Models that allowed me to scale my basic knowledge into a full-blown platform, something that would have been impossible with a traditional approach. The entire codebase was generated primarily by Gemini 2.5 Pro, with some help from Claude Sonnet and ChatGPT, based on my prompts — some detailed, some not so much. This article, and the product itself, is a living experiment. An attempt to probe the boundaries of what’s possible in this new reality we’ve all found ourselves in. My lack of commercial experience is both a weakness and a strength. A weakness because I probably reinvented the wheel and stepped on every rake imaginable. A strength because it meant I had no biases. I could approach the AI’s suggestions with a completely open mind.
Project Metrics
- Development Time: ~6 months
- AI Interactions: ~1500+ conversations
- Lines of Code: ~220,000 (Let me be clear: I don’t consider LOC a productivity metric. It’s a proxy for the project’s scale. This number includes data models, API endpoints, UI components — which the AI generates incredibly fast — and tests. It does not include dependencies.)
- File Count: 540+
- Tests: 400+
- Development Cost: $0 (This doesn’t include six months of my life. 90%+ of the code was generated in aistudio.google.com.)
A note on metrics: These figures are based on a codebase analysis and my personal experience. This isn’t a scientific study, just the subjective assessment of one “developer.”
Why Did This Experiment Start?
The idea was born from a simple desire: to create a scalping bot that thinks like a human scalper, making decisions based primarily on the order book and trade tape.
After some research, I found the market was split into two extremes:
- Simple Builders (3Commas, Bitsgap): Accessible, but completely inflexible. Basic GRID bots, some ability to trade on **TradingView **indicators.
- Professional Frameworks (QuantConnect, Backtrader): Incredibly powerful, but require serious programming skills.
Neither worked for me. I was looking for a tool with the flexibility of a framework (QuantConnect) but the simplicity of a builder (3Commas). When I couldn’t find it, I decided to build it myself. And when LLMs with million-token context windows appeared (hello, Gemini), I decided to put the idea to the test. That’s how DepthSight was born.
The “Infinite Flow” Phenomenon: Intensity Doesn’t Equal Burnout
Before diving into the technical guts, I want to share an observation that might seem counter-intuitive to some, and obvious to others. Without it, this story is incomplete.
Over these 6 months, I worked an average of 10–12 hours a day, often more, usually without weekends. And I didn’t burn out. In fact, I woke up every morning eager to continue.
How is this different from classic development?
In a traditional workflow, most of your time is spent on:
- Hunting for syntax errors.
- Reading documentation.
- Debugging bizarre behavior.
With an AI, you spend most of your time on architectural decisions, not fighting with syntax.
It’s like switching from a manual to an automatic transmission.
The result: A massive project completed in 6 months.
I wasn’t “grinding”; I was in a constant dialogue with a machine, and I saw tangible progress every 15 minutes. This fundamentally changed my entire perception of software development.
Important caveat: This “flow state” was, of course, possible because it’s my own project. I wasn’t dealing with legacy code or getting every decision approved by a committee.
Stack and Architecture: From “Suggest a Stack” to a Production-Ready System
My first prompt to the AI was rather naive:
Are you familiar with scalping? What rules from scalping can be adapted for a trading bot?
After a few hours of discussing my ideas and market observations, the AI (it was ChatGPT at the time) and I iteratively arrived at the following plan, which I documented for myself:
*1. Module Goals & Principles *Style: Scalping — high-frequency trades with small R:R Integration: Uses data (coin lists, candles, volumes, NATR) from the main app, no duplicate data fetching logic. Trading: Exclusively via Binance (ccxt or ccxt.pro). Toggleable: The plugin can be started and stopped without recompiling or restarting the entire application. Self-learning: Lightweight daily adjustments to key parameters based on trade history. *2. Interaction with Main Application *Data Source: Main app updates a JSON file or publishes via a local WebSocket every second/minute with a list of promising coins (filtered_pairs.json), e.g., where natr > 2. Consumer Bot: The bot module subscribes to these updates and maintains an in-memory pool of pairs to trade. Unified Config: All key parameters (filters, risk management, timeouts) are stored in
config/bot_config.py. *3. Bot Module Architecture **/myapp /bot_module __init__.py config.py data_consumer.py strategy.py executor.py risk_manager.py trainer.py controller.py logger.py /data filtered_pairs.json main.py *3.1. data_consumer.py: Readsfiltered_pairs.json, subscribes to updates. 3.2. strategy.py: Contains all scalping scenario detectors (volume breakouts, bounces off order book depth, spike-and-fade, etc.). Outputs a signal: (pair, direction, entry, stop, take, mode). 3.3. executor.py: Interface to Binance API. Methods:open_order(),close_order(), etc. 3.4. risk_manager.py: Calculates position size, controls max daily drawdown. 3.5. trainer.py: Daily (scheduled) job to collect trade logs, run light optimization (Grid/Bayes) on key parameters, and updateconfig.pyif a better configuration is found. 3.6. controller.py: The orchestrator. Gets pairs, asksstrategyfor signals, passes them torisk_managerand thenexecutor. 3.7. logger.py: Logs everything to a DB or CSV for thetrainer. *4. Data Flow Main app → updatesfiltered_pairs.json data_consumer→ signals new pairscontroller→ requests signals fromstrategy strategy→ returns Signal or Nonecontroller→ asksrisk_managerfor size/risk checkrisk_manager→ returns Approved/Deniedcontroller→ sends order toexecutoron approvalexecutor→ places order, tracks executioncontroller→ logs trade vialogger trainer(scheduled) → learns from logs, updatesconfig.pyPhaseDescriptionDeadlineMVP*data_consumer*+*strategy*(basic) +*executor*(basic) +*risk_manager*(simple) + **logger*2 weeksv1.0All scalping scenarios, news filter+3 weeksv1.1Add*trainer*(batch learning), runtime config updates+2 weeksv2.0UI/CLI for control, Telegram notifications+1 monthv3.0Dashboard monitoring, A/B testing, Dockerization+1 monthThis plan provides a clear roadmap from prototype to production-level solution. Good luck with the implementation!
This architecture later became the foundation of the platform. It’s also worth noting that I already had one smaller project under my belt: a scalping screener that monitored volatility and order book depth across 7 exchanges. It still serves as a data provider for the trading module today.
The Real Stack “Under the Hood”
A simple web stack wouldn’t handle the load. As development progressed, on the AI’s advice, the system grew to include more serious tools:
Numba: To accelerate critical Python calculations, especially in the controller core.River: A library for online machine learning.LightGBM / XGBoost: Used for batch offline model training.DEAP: A framework for genetic algorithms, forming the basis of a future feature for evolving profitable strategies.Pandas-ta: The workhorse for calculating dozens of technical indicators.
The Development Process: Real Problems, Real Prompts
I acted as the project manager, and the AI was my development team. Sometimes I gave it precise specs, but often, brilliant solutions emerged from simple dialogue. I’d just ask what options existed for a certain feature, and the AI would propose several, from which I could choose what I thought was best.
Deep Dive: Building the Executor—The Heart of the Trading Bot
The AI’s proposed architecture was logical. But as always, the devil is in the details. The most complex and critical component at the very beginning was executor.py—the module responsible for direct interaction with the exchange’s API. There was no room for error here; it controls real orders.
I decided to forgo ready-made libraries like ccxt in favor of a direct implementation using aiohttp and websockets. Why? In my previous screener project, I used ccxt and, despite its convenience, ran into its limitations in speed and reliability for low-latency tasks like scalping. So, I consciously decided to write a direct implementation to achieve the necessary performance and fault tolerance.
My first prompt to the AI focused on creating a fully asynchronous BinanceExecutor class that would use aiohttp to send orders (POST /api/v3/order) and websockets to connect to the User Data Stream.
Iteration #1: The Naive Client
The first version generated by the AI was simple and functional. It could sign REST requests and place orders.
# First version: placing an orderasync def place_order(self, symbol: str, side: str, quantity: float): endpoint = "/api/v3/order" params = { 'symbol': symbol, 'side': side.upper(), 'type': 'MARKET', 'quantity': quantity, } # (Request signing logic) async with self.session.post(self.base_url + endpoint, params=params) as response: return await response.json()
The problem was immediately obvious: after sending an order, we know nothing about its fate. Did it execute? At what price? Making REST calls every second to check the status (GET /api/v3/order) is slow, inefficient, and quickly burns through API limits. Unacceptable for scalping.
Iteration #2: Adding WebSockets
The solution is the User Data Stream. It’s a personal WebSocket channel where the exchange pushes real-time updates about your account: order fills, balance changes, etc.
So, I refined the task for the AI: add a start_user_data_stream method to BinanceExecutor. This method needed to get a listenKey via REST, connect to the WebSocket, and loop through incoming messages.
The AI generated working code, but after a few minutes of testing, the connection would drop.
Problem #1: Connection Drops. Networks are unreliable. Without automatic reconnection, the system is not viable.
Problem #2: listenKey Expiration. The key for the userData stream only lives for 60 minutes and needs to be refreshed periodically.
Iteration #3: Reconnection and Keep-Alives
I went back to the AI, described the problems, and formulated detailed technical requirements for reliability. The key points of my new prompt were:
- Modify
start_user_data_streamto wrap the main message listening loop in awhile Truefor automatic reconnection on any disconnect. - Add exponential backoff between reconnection attempts (5s, 7.5s, 11s…).
- Create a separate background task (
asyncio.Task) that sends a PUT request to/api/v3/userDataStreamevery 30 minutes to extend thelistenKey’s life.
This led to a much more complex, but robust, architecture within the Executor.
Case Study: A Specific Environment Bug (The Battle with aiodns on Windows)
Once the basic logic was ready, I hit a wall. When running the bot on Windows, it would crash with this error:
RuntimeError: aiodns needs a SelectorEventLoop on Windows.
I fed the full traceback to the AI and asked what was wrong. The first few answers were generic: “try updating your libraries,” “use a different asyncio policy.” Nothing worked. The breakthrough came when I formulated a more specific role and task for the AI. I asked it to act as a senior Python developer with experience debugging asyncio on Windows and posed a highly specific question: how can I force aiohttp to not use aiodns and switch to its default threaded DNS resolver, without changing the global asyncio policy for the entire application?
The answer was instant and precise:
“The problem is that
*aiohttp*automatically uses*aiodns*if it’s installed. To force a different resolver, create a*TCPConnector*with an explicitly specified*ThreadedResolver*and pass it to the*aiohttp.ClientSession*.“
# The fix for the aiodns issuefrom aiohttp import ThreadedResolver, TCPConnector, ClientSession
resolver = ThreadedResolver()connector = TCPConnector(resolver=resolver)session = ClientSession(connector=connector)
It worked. The lesson: sometimes you don’t just describe the problem; you ask the AI how to achieve a specific technical outcome, guiding it in the right direction.
Final Code: Putting It All Together
After all the iterations and fixes, the core part of the Executor responsible for the userData stream looked like this. This code is the result of dozens of refinements and corrections proposed by the AI in response to my prompts.
# bot_module/executor.py # To be honest, in the first version, I had no idea # how to manage the lifecycle of these tasks. I had to Google # and ask a lot of questions about asyncio.async def _user_data_ws_listener_loop(self): """ Main loop that gets a listenKey, connects to the WS, and ensures automatic reconnection. """ reconnect_delay = 5 # Initial delay while self._user_data_running: try: # 1. Get the connection key listen_key = await self.get_listen_key() if not listen_key: logger.error(f"Failed to get listenKey, retrying in {reconnect_delay}s...") await asyncio.sleep(reconnect_delay) continue ws_url = f"{self.ws_base_url}/ws/{listen_key}" # 2. Establish WebSocket connection async with websockets.connect(ws_url, ping_interval=20, ping_timeout=10) as ws: self._user_data_ws = ws logger.info("User data WebSocket CONNECTED.") reconnect_delay = 5 # Reset delay on success # 3. Start the background keep-alive task keepalive_task = asyncio.create_task(self._user_data_keepalive_loop(listen_key)) # 4. Listen for incoming messages async for message in ws: if not self._user_data_running: break try: data = json.loads(message) # Invoke the callback passed from the Controller await self._user_data_callback(data) except json.JSONDecodeError: logger.warning(f"Received non-JSON user data: {message[:200]}") # Cancel the keep-alive task if we exit the loop keepalive_task.cancel() except (ConnectionClosed, asyncio.TimeoutError, WebSocketException) as e: logger.warning(f"User data WS connection error: {e}. Reconnecting in {reconnect_delay}s...") except Exception as e: logger.error(f"Unexpected error in WS listener loop: {e}", exc_info=True) finally: self._user_data_ws = None if self._user_data_running: await asyncio.sleep(reconnect_delay) reconnect_delay = min(reconnect_delay * 1.5, 60) # Exponential backoff up to 1 minute async def _user_data_keepalive_loop(self, listen_key: str): """Periodically extends the listenKey's lifetime in the background.""" while self._user_data_running: await asyncio.sleep(config.USER_DATA_PING_INTERVAL) # Wait 30-50 minutes if not self._user_data_running: break logger.info(f"Attempting to keep-alive listenKey...") await self.keep_alive_listen_key(listen_key)
Takeaways from Developing the Executor
- Reliability is Everything: For a trading bot, it’s not enough for code to just “work.” It must be resilient to network failures, API errors, and environment issues.
- Async is Not Magic: Managing multiple background tasks (
listener,keep-alive) requires a clear understanding ofasyncio.Taskand their lifecycle. - The AI is a Powerful Debugger: When prompted correctly, an AI can diagnose complex, environment-specific bugs, saving days of work.
Developing this single component was the most challenging, but also the most critical, part of building the trading module. It laid the foundation for the stability and speed of the entire bot, and much of the code has been rewritten several times since.
Problem #2: Generating a Vectorized Backtester Core
To implement a genetic algorithm for strategy discovery, I needed to solve a performance bottleneck. Running thousands of full backtests would take an eternity. I asked the AI to create a fast backtester using Pandas, and it gave me inefficient code full of .iterrows() loops. I refined the prompt, formulating stricter requirements for a vectorized backtester core, emphasizing the need to avoid loops in favor of vectorized Pandas and NumPy operations.
My technical prompt looked like this:
- Generate a Python class that takes a DataFrame with OHLCV columns and two boolean columns: ‘enter_long’ and ‘exit_long’.
- Implement a
run()method to simulate trades. - Identify entry points where ‘enter_long’ becomes True and no position is open.
- For each trade, calculate fixed SL and TP levels as a % of the entry price.
- Vectorize the logic to find which target (SL or TP) was hit first on subsequent candles.
- Collect a trade log (entry, exit, price, pnl) and return final KPIs.
- Use
np.where,.shift(),.cumsum(), and other vectorized methods.
After a few iterations, I had a working, high-speed core for the initial evaluation of genetic strategies. The module still needs more work and testing, but early results show a ~100x speedup compared to a standard, loop-based backtester.
These are just two examples out of thousands of conversations that led to the creation of DepthSight, a platform that otherwise would not exist. The AI may not write perfect code, but neither do many humans. In any case, the future is already here. What was impossible yesterday is reality today.
The Result: A Platform Where AI is Not Just the Tool, but the Product
This entire process led to a product where AI is directly involved in creating and testing the trading strategies themselves.
1. The AI Assistant: From Trader Jargon to a Working Algorithm
The killer feature is parsing complex trading ideas described in natural language directly into ready-made blocks in the visual editor. A trader can describe their system just as they would in a notebook. Here’s a real example of a text description (original in Russian) that the AI assistant takes as input:
[Original complex Russian trading prompt describing multi-part entries, cascading breakouts, stop-loss logic based on position size, and multiple market conditions like order book pressure, volume spikes, and correlation with BTC.]
The AI assistant parses this text and, in under a minute, generates a complete strategy structure in the visual editor. You can then fine-tune it by hand and immediately send it for backtesting.
Press enter or click to view image in full size
Generating a strategy from text
2. Iterative AI Analysis: Turning Losses into Data
After every backtest, there’s an “Analyze with AI” button. The assistant gets access to all trades and their context, including why certain signals were rejected and, crucially, which combinations of conditions worked or failed. This helps it find non-obvious patterns and provide recommendations.
Example AI Analysis: “I’ve analyzed your 58 trades. 80% of your losing positions were opened when the BTC volatility index was above the 75th percentile. Recommendation: Add a filter that blocks entries if the ATR on BTC’s 5-minute chart exceeds X. Would you like me to generate a new version of the strategy with this filter?”
3. Weighted Conditions: Coding a “Trader’s Gut Feeling”
This is our answer to the binary “YES/NO” logic of every other strategy builder out there. Each condition for entering a trade has not just a toggle, but a “weight.”
- Order book depth? Weight 30.
- Level breakout? Weight 50.
- Tape acceleration? Weight 20.
You set an “activation threshold,” for example, 70. An entry signal will only trigger if a combination of conditions with a total weight of >= 70 is met. In this case, that would be level breakout + tape acceleration (70) or level breakout + order book depth (80). This allows traders to formalize their intuition, where a decision is based on a confluence of factors, not a single rigid trigger.
Given the complexity of a “prompt” that considers the trade tape, order book, open interest, correlation, partial entries/exits, AI-assisted backtest analysis, and weighted conditions, I’m not sure if anything like this exists elsewhere. Please let me know in the comments if you’ve seen it.
Visualizing Project Complexity
At one point, I asked the AI to analyze the entire codebase and generate a GraphML file to visualize the architecture. Here’s the project map.
Press enter or click to view image in full size
Project Map
Why is AI a Genius for Some, and an Idiot for Others?
Before writing this, I read countless discussions about AI-driven development and noticed a strange paradox.
Some people write:
“LLMs are a revolution. It built something I never could have.”
Others (often more experienced developers) write:
“Tried it. It’s garbage. The code doesn’t work.”
It’s the same tool, but the results are diametrically opposed.
Why?
My Hypothesis: It’s Not the AI, It’s Your Mental Model
I’ve concluded that your effectiveness with an AI depends on one critical factor: your willingness to accept that the AI might know more than you do. This, in turn, allowed me to ask questions and seek truth in our dialogues.
It sounds absurd. “How can a machine know more than a senior developer with 15 years of experience?”
But the fact is, the AI has been trained on billions of lines of code. It has seen patterns across every programming language imaginable, patterns a senior dev has never used. It knows solutions to problems that thousands of other developers have faced.
Objectively, the statistical model has a broader knowledge base than any single human.
Two Models of Interaction
Approach 1: “Do as I Say”
- You say: “Code X this way.”
- The AI does it differently.
- You think: “Wrong. Redo it.”
- Result: Constant frustration.
Approach 2: “How Would You Do It?”
- You ask: “What’s the best way to solve X?”
- The AI suggests an unfamiliar solution.
- You think: “Interesting. Why this way? Explain.”
- Result: New knowledge + working code.
Guess which model produces a “genius” and which produces an “idiot”?
Why My AI is a “Genius”
Because I accepted a simple truth: The AI has seen more code than I will ever see in my lifetime.
Yes, it can be wrong. Yes, it hallucinates sometimes. But when it suggests a solution I don’t recognize, my first thought isn’t, “That’s nonsense.” My first thought is, “That’s interesting. Why this way? What can I learn here? What are the other options?”
This isn’t me saying I’m dumb. It’s me objectively acknowledging that the model’s knowledge base is wider than my own.
What About the Cons?
Of course, it wasn’t all smooth sailing. There were moments of sheer frustration where I felt the AI had become “dumbe