Iurii RoguliaIurii Rogulia
AboutServicesPricingProjectsSkillsStackReviewsPhrasesBlog
Contact
Iuriiย ships.

Iurii Rogulia, senior full-stack software engineer. Professionally building software since 2001.

Think of a number
PricingQuality checklistPrivacy PolicyCookie Policy

Business

TMI Iurii Rogulia
VAT ID: FI29845875
DUNS: 368664211
Lappeenranta, Finland ๐Ÿ‡ซ๐Ÿ‡ฎ

Back to projects

Iuriiย Automates: Perpetual Futures Grid Trading System

October 1, 2025

Production algorithmic trading system for perpetual futures โ€“ multi-account, event-driven, with a dynamic progressive grid and 10-level risk management.

Stack

Python

Libraries

pybitpandaspytestPyYAMLpython-dotenv

Services

BybitTelegram

Topics

Algorithmic TradingGrid TradingFuturesRisk ManagementCrypto

Key Results

  • v2.12.0 โ€“ running in production since October 2025
  • 43,770 lines of Python โ€“ 41 source files, 19 test files, 35 scripts
  • 10-level risk management with automatic emergency response
  • 6-step crash recovery reconstructing full state from exchange
  • 248 commits over 4.5 months of active development
Perpetual Futures Grid Trading System

Overview

A proprietary algorithmic trading system for perpetual futures markets, built entirely from scratch. The system manages multiple isolated trading accounts simultaneously, each running independent dynamic grid strategies across multiple symbols โ€“ with shared WebSocket connections to price feeds to minimize exchange load.

This is a closed-source personal project. The description focuses on architecture and engineering decisions, not trading strategy or performance.

Architecture

The system is a modular monolith designed with SaaS-ready multi-tenancy from the start:

MultiAccountBot (orchestrator)
  โ†’ TradingAccount[] (per account)
      โ†’ GridStrategy[] (per symbol)
          โ†’ StateManager, OrderExecutor, EventQueue, TPCalculator
      โ†’ BybitClient (REST), BalanceManager, ProfitProtectionManager
  โ†’ PublicWebSocket[] (shared per symbol+environment)
  โ†’ PrivateWebSocket[] (per account)

Key principle: each account is fully isolated โ€“ separate logs, state files, and emergency flags. But public WebSocket connections to price feeds are shared across accounts trading the same symbol, reducing exchange connections.

Event-Driven Threading

The system uses clean threading (not asyncio) โ€“ a deliberate choice for financial systems where predictability matters more than throughput. WebSocket callbacks are non-blocking: they put events into a priority queue, and a dedicated EventWorker processes them sequentially.

# Priority queue: Execution events (P0) before Order events (P1)
# Prevents order state from being updated before execution is processed
heapq.heappush(self._queue, (priority, timestamp, event))

Eight explicit RLock/Lock instances guard shared resources across components. No shared mutable state without a lock.

State Persistence

No database โ€“ state is stored in JSON files with atomic writes:

# Atomic write: temp file โ†’ rename (OS-level atomic on Linux)
with tempfile.NamedTemporaryFile(mode='w', dir=dir, delete=False) as f:
    json.dump(state, f, indent=2)
    temp_path = f.name
os.replace(temp_path, state_path)  # atomic

This means a crash mid-write never corrupts the state file. The exchange API is the single source of truth on restart.

Dynamic Progressive Grid

Grid step size is not fixed โ€“ itโ€™s calculated from market volatility:

# EMA of daily ATR% over 10 periods, scaled to 10-day horizon
atr_ema = pandas_ewm(daily_atr_pct, span=10).mean().iloc[-1]
base_step = atr_ema * sqrt(10)
 
# Steps increase with level: L1 is minimum, L10 is ~3.16ร— larger
step[n] = base_step * scale * sqrt(n)

This is computed once on a fresh start and then immutable โ€“ preventing configuration drift across restarts.

Position sizing uses martingale doubling: qty[level] = base_qty ร— 2^level. Base quantity is also locked after initial calculation.

Risk Management โ€“ 10 Levels

LevelTriggerResponse
1IM Rate โ‰ฅ 90%Block workflow, move balance orders to 0.01% from market
2High IM Rate + profitClose position
3Trailing stopLock profit on drawdown from peak
4Position value > 40% of risk limitBlock new orders
5Position limit timeoutForce close stuck positions
6Both sides profitableClose both simultaneously
7TP safety check (hysteresis 5%/10%)Pause TP if insufficient margin for reopening
8TP safety pre-checkBlock protective orders if margin insufficient
9Level rate limitBudget: ceil(max_levels ร— 0.5) level-ups per 48h sliding window
10Emergency stopFile-based flag requiring manual reset

Level 7โ€“8 deserve explanation: at high grid levels, taking profit would leave insufficient margin to reopen the position. Without this check, the system would keep averaging down (wasting capital) while TP remains unreachable. The solution: block protective orders so TP stays active and the position can actually close.

Crash Recovery

On restart, a 6-step workflow reconstructs full state from the exchange:

1. Cancel all open orders
2. Clear local state
3. Fetch actual positions from exchange
4. Detect state: none / long_only / short_only / both
5. Place take-profit orders
6. Place balance + protective orders

The position_level_analyzer.py (2,354 lines) reverse-engineers grid levels from order history, with backward compatibility for 5+ historical orderLinkId formats across versions.

Hard Problems

Independent Cycle Tracking (v2.8.0)

LONG and SHORT sides take profit at different frequencies. A shared cycle counter caused a subtle bug: SHORT would TP 3ร— (cycleโ†’13) while LONG was still at L5 (cycle=10). On restart, the restoration logic searched for LONG positions using cycle=13 โ€“ found nothing โ€“ and triggered Emergency Stop.

Fix: independent long_cycle/short_cycle counters with L/S prefixes in orderLinkId. Simple in retrospect, invisible until it happened in production.

AMEND Workflow Removal (v2.3.5)

Originally, order modification used Bybitโ€™s AMEND endpoint. This caused a systemic failure: Bybitโ€™s Order History API sorts by createdTime, and AMEND preserves the original timestamp. Amended orders couldnโ€™t be found by the restoration logic โ†’ Emergency Stop on every restart.

Fix: remove AMEND entirely. Cancel + replace instead. More API calls, zero ambiguity.

Partial Fill Race Condition

Discovered after a real incident (2025-11-01): a partial fill triggered the workflow while the remaining quantity was still being filled. Two concurrent workflows โ†’ inconsistent state.

Fix: _partially_filled_freeze flag suspends the workflow until fill is complete or times out.

Scale

MetricValue
Source code27,677 lines (41 files)
Tests7,939 lines (19 files, 280 functions)
Scripts8,154 lines (35 diagnostic tools)
Documentation17 markdown files, 385KB
Total Python~43,770 lines
Commits248
DevelopmentOct 2025 โ†’ present
Versionv2.12.0
StatusRunning in production (systemd service)

Project Review

โ€œ

The bot has been running non-stop since deployment without a single manual restart.

James Whitfield ๐Ÿ‡จ๐Ÿ‡พ

Private Trader

Stack

Python

Services

Bybit

Topics

Algorithmic TradingGrid TradingCrypto
Iurii RoguliaAvailable

Need something similar?

I build custom solutions โ€“ from APIs to full products. Letโ€™s talk about your project.

View all projects

Related projects

pikkuna.fi โ€“ AI-Powered Localization Pipeline
pikkuna.fi โ€“ AI-Powered Localization Pipeline
July 30, 2026
pikkuna.fi โ€“ AI-Powered Localization Pipeline

A production localization pipeline built entirely on the OpenAI API, with one English source of truth and SEO-aware translation prompts.

Stack

Python

Libraries

pytestOpenAI SDKpython-dotenvpycountry

Services

OpenAIAnthropic

Topics

AILLMPrompt Engineeringi18nLocalizationSEOAutomationTestingArchitectureCTO
Site Pulse โ€“ Open-Source Website & Domain Health Monitor
December 11, 2024
Site Pulse โ€“ Open-Source Website & Domain Health Monitor

Open-source Python tool that discovers URLs from sitemap.xml and validates each in desktop and mobile modes with parallel checks.

Stack

Python

Libraries

requestspython-whoispytzpython-dotenv

Services

Telegram

Topics

MonitoringAutomation

Related posts

Bybit Grid Trading Bot in Python: Architecture and Risk
April 19, 2026ยท 14 min
Bybit Grid Trading Bot in Python: Architecture and Risk

Bybit grid trading bot in Python for live futures: event-driven architecture, ATR-based grid steps, 10-level risk management, atomic state, pytest suite.

Stack

Python

Libraries

pybitpytestpandasPyYAMLpytzrequestspython-dotenv

Services

BybitTelegram

Topics

Algorithmic TradingGrid TradingFuturesRisk Management
Du for the Owner, Sie for the Client: An i18n Linter That Checks Register Per Audience
October 7, 2026ยท 11 min
Du for the Owner, Sie for the Client: An i18n Linter That Checks Register Per Audience

An i18n linter that checks address register per audience, not just per language, plus key order and drift across a 32-locale JSON translation set.

Stack

Python

Libraries

next-intl

Topics

i18nDeveloper ToolsTestingQuality