<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: DolphinDB</title>
    <description>The latest articles on DEV Community by DolphinDB (@dolphindb).</description>
    <link>https://dev.to/dolphindb</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F766486%2Fde2034e3-e2f4-433c-8a5b-4b1330770161.png</url>
      <title>DEV Community: DolphinDB</title>
      <link>https://dev.to/dolphindb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dolphindb"/>
    <language>en</language>
    <item>
      <title>We Rebuilt a Securities Firm's FICC Data Stack — Queries Got 10x Faster</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Thu, 16 Jul 2026 07:21:39 +0000</pubDate>
      <link>https://dev.to/dolphindb/we-rebuilt-a-securities-firms-ficc-data-stack-queries-got-10x-faster-7ib</link>
      <guid>https://dev.to/dolphindb/we-rebuilt-a-securities-firms-ficc-data-stack-queries-got-10x-faster-7ib</guid>
      <description>&lt;p&gt;The investment research data team at a leading publicly listed securities firm had long struggled with fragmented data, inconsistent data standards, and insufficient real-time computing capabilities across its FICC operations. The journey from strategy development to production deployment often took several months.&lt;/p&gt;

&lt;p&gt;To overcome these challenges, the firm partnered with DolphinDB to build a unified FICC multi-asset research data platform based on an integrated stream-batch architecture, achieving:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;More than a 10× improvement in historical market data query performance&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Millisecond-level latency for intraday market data&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A reduction in the strategy development-to-production cycle from three months to just two weeks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An overall efficiency improvement of more than 85%&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The platform eliminated data silos and fragmented system architectures, providing a solid technical foundation for FICC quantitative trading and market-making.&lt;/p&gt;

&lt;h2&gt;
  
  
  FICC Quantitative Trading: Speed Creates Alpha
&lt;/h2&gt;

&lt;p&gt;FICC markets have long been dominated by over-the-counter (OTC) trading, where research and trading workflows depend heavily on manual data handling. Quantitative teams spend significant time integrating and cleaning data before pricing, backtesting, and risk analysis. The result is slower research, less accurate pricing, and delayed hedging.&lt;/p&gt;

&lt;p&gt;FICC markets are becoming increasingly data-driven. Growing market complexity and stronger links between exchange-traded and OTC markets are driving demand for unified data management, real-time analytics, and faster strategy execution.&lt;/p&gt;

&lt;p&gt;As a result, low-latency processing of high-frequency market data and unified data governance have become essential capabilities for modern FICC quantitative platforms, serving as the technological foundation for high-precision pricing, algorithmic hedging, and automated market making at leading financial institutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge: Data Fragmentation Was Slowing Down the Business
&lt;/h2&gt;

&lt;p&gt;As business requirements evolved, the firm's traditional FICC data architecture could no longer support the growing demands of quantitative research and trading. Four major challenges emerged.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Inconsistent Data Across Sources&lt;/strong&gt;&lt;br&gt;
FICC research data came from multiple heterogeneous sources, including CTP market data feeds, third-party vendors, and locally maintained Excel spreadsheets. Each used different schemas, field definitions, and transmission formats. The lack of standardized frequently resulted in typo records, missing sequences, and other data quality issues, reducing backtesting accuracy and reinforcing the classic “Garbage In, Garbage Out” (GIGO) cycle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Fragmented Architecture and Isolated Data Components&lt;/strong&gt;&lt;br&gt;
Years of incremental IT expansion had left the same data duplicated across multiple systems. Researchers had to retrieve, reconcile, and integrate data across systems before analysis, while the siloed architecture increased infrastructure costs through duplicated hardware, redundant storage, and higher maintenance overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Batch-Oriented Processing with Limited Real-Time Computing&lt;/strong&gt;&lt;br&gt;
The legacy architecture relied on end-of-day batch processing, with analytical results available only on T+1. High-frequency strategies simply weren't possible without live market data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. High Technical Barriers for Researchers&lt;/strong&gt;&lt;br&gt;
The fragmented architecture placed a heavy technical burden on researchers. Instead of focusing on strategy development, they spent much of their time writing data extraction scripts and glue code to integrate data from multiple sources. As a result, development cycles became longer and production deployment was significantly delayed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl2hxq19cuxn3dhmuurbk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl2hxq19cuxn3dhmuurbk.png" alt=" " width="800" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: An FICC Research Data Platform Powered by DolphinDB
&lt;/h2&gt;

&lt;p&gt;The firm partnered with DolphinDB to build a unified, multi-asset FICC research data platform on a distributed time-series database, combining streaming and batch processing in a single framework.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdh3m7fz9thm1oearpvqw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdh3m7fz9thm1oearpvqw.png" alt=" " width="800" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqxqeuhxd8p8wxbxl3yd0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqxqeuhxd8p8wxbxl3yd0.png" alt=" " width="800" height="574"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Consolidating Historical Data
&lt;/h2&gt;

&lt;p&gt;The team migrated nearly five years of cash bond market data from Hive into DolphinDB, along with historical futures and options data. With DolphinDB's multi-model storage, the platform optimizes storage for different data types while balancing storage efficiency and query performance.&lt;/p&gt;

&lt;p&gt;Multi-source data is cleansed and normalized into a unified trade and quote data model. A closed-loop validation process continuously checks for missing or abnormal records, enabling centralized management of historical data across all asset classes while significantly reducing system complexity and maintenance costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a Real-Time Market Data Hub
&lt;/h2&gt;

&lt;p&gt;The system uses DolphinDB's Kafka plugin to build a low-latency data ingestion layer, enabling real-time access to market data from major exchanges and brokers through a unified data pipeline. Combined with DolphinDB's streaming engines, the system enables real-time calculation of derived indicators. As new data arrives, incremental computations are triggered automatically and results are generated with minimal latency.&lt;/p&gt;

&lt;p&gt;For FICC-specific needs, DolphinDB also provides a real-time one-minute candlestick generation module for futures and options. The module addresses common industry challenges such as inconsistent exchange timestamps and disconnected workflows between historical and real-time data processing. Manual and error-prone alignment processes are now automated within the system.The module now serves as a core infrastructure component, feeding low-latency, high-availability minute-level data to both production trading systems and quantitative strategies.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Results
&lt;/h2&gt;

&lt;p&gt;The unified platform now connects research, trading, and risk management on a single data foundation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd743vi2p9quzi9oug16c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd743vi2p9quzi9oug16c.png" alt=" " width="600" height="196"&gt;&lt;/a&gt;&lt;br&gt;
The project transformed fragmented, post-market data workflows into a unified real-time data foundation. By connecting research, trading, and risk management teams on a single data platform, it enables faster decision-making, accelerates quantitative research, and strengthens the firm's competitiveness in FICC market making.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next: AI-Powered Research and High-Fidelity Backtesting
&lt;/h2&gt;

&lt;p&gt;For both the firm and DolphinDB, this project marks a major milestone in modernizing FICC data infrastructure—but it is only the beginning.&lt;/p&gt;

&lt;p&gt;Looking ahead, we plan to further evolve the platform in two strategic directions.&lt;/p&gt;

&lt;p&gt;The first is deeper AI integration. DolphinDB will build a high-performance feature store designed for AI workloads, enabling factor selection, model training, and online inference directly within the data platform to accelerate the deployment of AI-driven investment research.&lt;/p&gt;

&lt;p&gt;The second is the development of a high-fidelity backtesting platform capable of dynamically simulating intraday margin requirements and real-time risk exposure for futures and options. By more accurately replicating live trading conditions, the platform will provide a more realistic environment for strategy validation and improve the reliability of quantitative research before production deployment.&lt;/p&gt;

&lt;p&gt;As financial markets continue to evolve, DolphinDB will keep innovating at speed—empowering intelligent finance with higher performance, greater reliability, and technology built for the future.&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>datascience</category>
      <category>database</category>
      <category>capitalmarkets</category>
    </item>
    <item>
      <title>What’s New in DolphinDB：Introducing DolphinX, an Enterprise-Grade Platform for Agent Development and Governance</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Wed, 15 Jul 2026 08:33:01 +0000</pubDate>
      <link>https://dev.to/dolphindb/whats-new-in-dolphindbintroducing-dolphinx-an-enterprise-grade-platform-for-agent-development-4emo</link>
      <guid>https://dev.to/dolphindb/whats-new-in-dolphindbintroducing-dolphinx-an-enterprise-grade-platform-for-agent-development-4emo</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fccdbexqnj6vyu4xofmoy.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fccdbexqnj6vyu4xofmoy.webp" alt=" " width="800" height="431"&gt;&lt;/a&gt;&lt;br&gt;
_This release is a Compatibility Level 2 update. For details, please refer to the V3.00.6 Release Notes.&lt;/p&gt;

&lt;p&gt;Features marked [V3.0] are available exclusively in DolphinDB 3.00.6._&lt;/p&gt;

&lt;p&gt;As enterprises race to turn AI from pilot projects into production systems, the real bottleneck is often integration — connecting data, compute, and workflows into something secure and reusable. With DolphinDB V3.00.6 &amp;amp; V2.00.19, we’re tackling this head-on.&lt;/p&gt;

&lt;p&gt;Central to this release is DolphinX, an enterprise-grade platform that deeply integrates DolphinDB’s native capabilities with an Agent platform — turning data, computing, and business logic into secure, reusable, and governable AI agent capabilities with minimal effort.&lt;/p&gt;

&lt;p&gt;This release also strengthens core capabilities across FeatureDB, FICC, JIT compilation, dynamic script optimization, high-availability stream computing, and system operations — spanning the full workflow from low-latency feature storage and complex derivatives pricing to high-performance script execution and enterprise-grade governance.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;1. DolphinX: An Enterprise-Grade AI Development and Governance Platform&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Available in V3.0&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As demand grows for natural language data querying, intelligent analytics, and automated workflows, the traditional “database + external AI framework” approach is increasingly challenged by concerns around security, compatibility, operational complexity, and enterprise governance. DolphinX, an enterprise-grade platform for AI agent development and governance, enables organizations to transform their data, computing, and business capabilities into secure, governable, and reusable AI agent services with a single integration, empowering faster business innovation and accelerating AI deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Natural Language-Driven Data Workflows&lt;/strong&gt;&lt;br&gt;
With the built-in Coding Agent, users can interact with DolphinDB’s core capabilities using natural language, eliminating the need to learn Dlang. From script generation and debugging to SQL query analysis, data import/export, stream processing, and real-time engine development, Coding Agent makes advanced data capabilities accessible to a much broader range of business users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Out-of-the-Box AI Toolkit with Flexible Extensibility&lt;/strong&gt;&lt;br&gt;
DolphinX works out of the box with zero configuration required. Just log in through the web interface to get started. It also includes a web-based management console for configuring prompts, models, and MCP servers, and attaching Skills to build dedicated AI agents.&lt;/p&gt;

&lt;p&gt;DolphinX comes with a rich set of built-in Skills covering coding standards, data import, financial services, stream processing, operations and maintenance, machine learning, data visualization, testing, and more (see the table below).&lt;/p&gt;

&lt;p&gt;For organizations requiring deeper customization, DolphinX exposes APIs that enable seamless integration of its AI capabilities into existing enterprise systems.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0l5yz2j8n5mtyr6jhtsr.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0l5yz2j8n5mtyr6jhtsr.webp" alt=" " width="640" height="721"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Eliminate External Dependencies and Build an Enterprise-Grade Intelligent Closed Loop&lt;/strong&gt;&lt;br&gt;
AI agent capabilities are built directly into the DolphinDB Server, eliminating the need for third-party orchestration frameworks. This enables a complete, end-to-end workflow — from understanding and analysis to execution — entirely within a single system. For scripts generated by AI models but not yet reviewed by users, DolphinX parses and validates them before execution, blocking high-risk operations such as deletion and cleanup. When invoking DolphinDB capabilities, AI agents inherit the current user’s permissions, eliminating the need for a separate access control system. This significantly reduces the security risks, compatibility issues, and operational complexity associated with external dependencies, making enterprise AI applications more secure, reliable, and efficient.&lt;/p&gt;

&lt;p&gt;To further enhance AI’s understanding of database environments, DolphinDB has also expanded its semantic metadata framework for database objects. Building on existing support for descriptions of tables, table columns, and user-defined functions, this release introduces description support for both the Catalog and Schema levels. In addition, the maximum length of table column descriptions has been increased from 256 bytes to 1,024 bytes. By providing richer representations of database hierarchy and business semantics, AI agents can more accurately understand data assets, generate scripts, and execute analytical tasks, resulting in a more reliable enterprise-grade intelligent closed loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enterprise-Grade Agent Development and Governance Foundation&lt;/strong&gt;&lt;br&gt;
DolphinX provides a common capability layer for building and governing Agents, including session management, Memory, Skills, context assembly, LLM provider management, and permission auditing. This helps enterprises avoid rebuilding these capabilities from scratch for every Agent, while enabling unified governance and operations.It supports both short-term and long-term memory, and provides a built-in RAG knowledge system based on DolphinDB’s TextDB and VectorDB. It also supports unified integration with multiple LLM providers, including fallback switching, token budget management, and local or private cloud deployment, balancing flexible extensibility, data security, and autonomous control.&lt;/p&gt;

&lt;p&gt;Designed for scenarios such as natural language interaction, intelligent analytics, and automated processing, DolphinX deeply integrates AI capabilities with DolphinDB to provide an integrated product ecosystem that combines out-of-the-box usability with enterprise-level extensibility. It enables organizations to build intelligent data applications with lower barriers to entry and reduced system integration costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;2. FeatureDB: A Low-Latency Feature Storage Engine for AI and Machine Learning Applications&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Available in V3.0&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In AI and machine learning training and online inference scenarios, feature data access follows different patterns than general analytical queries with high-frequency reads by column, window, and timestamp. Traditional online feature stores, which are primarily designed for row-based access, focus more on full-row read/write operations and key-value retrieval. As a result, they are not fully optimized for these access patterns and can become performance bottlenecks in scenarios involving wide feature tables, frequent reads, and low-latency services.&lt;/p&gt;

&lt;p&gt;FeatureDB is purpose-built to address exactly this gap. Running within the DolphinDB environment, it offers a lightweight, high-performance foundation for feature serving pipelines in AI and machine learning applications.&lt;/p&gt;

&lt;p&gt;Note: The FeatureDB introduced in this release currently supports in-memory mode only. Persistent storage will be available in future releases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Microsecond-Level Random Reads to Alleviate Feature Access Bottlenecks&lt;/strong&gt;&lt;br&gt;
FeatureDB delivers microsecond-level random reads, with response times typically ranging from a few micro-seconds to tens of microseconds. This significantly reduces the impact of feature access latency on both training throughput and inference speed, especially in large-scale, high-frequency access scenarios.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Native Support for Machine Learning Data Types and Flexible Wide Table Design&lt;/strong&gt;&lt;br&gt;
FeatureDB natively supports commonly used floating-point data types in machine learning, including FLOAT8, FLOAT16, FLOAT32, and FLOAT64, covering different tradeoffs between precision and storage efficiency. It also supports wide tables containing hundreds of feature columns and provides simple syntax for quickly defining continuous feature columns, reducing the management complexity of large-scale feature engineering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Seamless Python Ecosystem Integration, Simplifying Workflows from Import to Retrieval&lt;/strong&gt;&lt;br&gt;
On the upstream side, FeatureDB integrates with feature computation tools such as Spark and PyArrow through the standard Parquet format, enabling one-click batch import. On the downstream side, its Python SDK directly outputs data that can be consumed by mainstream ecosystem tools such as NumPy and PyTorch, covering the complete pipeline from storage to model training and inference. By leveraging industry-standard tools and formats throughout the workflow, FeatureDB effectively reduces system integration and maintenance costs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzk6y6aqrm9wt6a8328m1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzk6y6aqrm9wt6a8328m1.webp" alt=" " width="640" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The introduction of FeatureDB marks an extension of DolphinDB’s capabilities in AI and machine learning scenarios, providing users with a new infrastructure option for building high-performance feature data services.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;3. Building a Complete Pricing Loop: Comprehensive FICC Capability Enhancements&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Available in V3.0&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The new release continues to enhance DolphinDB’s FICC pricing capability framework by introducing a wide range of pricing functions across fixed income, interest rates, credit, foreign exchange, and equity OTC derivatives. New support covers agreements, government bond futures, FRAs, Caps/Floors, Swaptions, CDS, NDFs, digital options, and range accrual options.&lt;/p&gt;

&lt;p&gt;In addition, the release introduces new foundational tools such as Copula functions, credit curves, volatility surfaces/cubes, date generation, and schedule construction. These enhancements further strengthen DolphinDB’s cross-asset pricing and risk analysis capabilities, enabling a more complete and integrated pricing workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvt1z2dl5zcgjnkggcc4k.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvt1z2dl5zcgjnkggcc4k.webp" alt=" " width="640" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fog7i8o5341lbogsbcp6x.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fog7i8o5341lbogsbcp6x.webp" alt=" " width="640" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;4. Enhanced JIT Capabilities with Broader Language Support and Greater Stability&lt;/strong&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Available in V3.0&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As enterprises increasingly demand real-time execution of complex business logic, high-performance script acceleration, and event-driven processing, Just-In-Time (JIT) compilation has become a critical foundation for executing core computational workloads. In production environments, users expect more than successful compilation — they also require comprehensive support for complex language features, rich object and container operations, and consistent performance under long-running workloads, complex event streams, and high-concurrency scenarios.&lt;/p&gt;

&lt;p&gt;DolphinDB has comprehensively upgraded its JIT engine in the new release, expanding language support while significantly improving runtime stability. These enhancements allow users to migrate more business logic into the high-performance execution path with minimal code refactoring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Broader Language Support for Complex Scripts&lt;/strong&gt;&lt;br&gt;
The new release adds JIT support for a wider range of language features, including partial application, anonymous functions, lambda expressions, closures, default parameters, and keyword arguments. As a result, many scripting patterns that previously relied on the interpreter can now be compiled directly without rewriting code specifically for JIT compatibility. This significantly improves scripting flexibility for scenarios involving reusable abstractions, complex computational workflows, and sophisticated data structures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enhanced Type System and Container Support&lt;/strong&gt;&lt;br&gt;
This release introduces explicit type annotations as a major enhancement to the JIT type system. Developers can declare member types directly within class definitions using the following syntax:&lt;br&gt;
&lt;code&gt;class MyClass {&lt;br&gt;
    field1 :: STRING&lt;br&gt;
    field2 :: INT&lt;br&gt;
    field3 :: DOUBLE VECTOR&lt;br&gt;
    field4 :: DICTIONARY[STRING, STRING]&lt;br&gt;
}&lt;/code&gt;&lt;br&gt;
Type annotations support scalar types, user-defined classes, vectors, ANY, and recursively nested dictionaries. The JIT compiler generates type-specialized machine code directly from these declarations, eliminating runtime type inference and significantly improving performance when accessing complex objects and nested data structures.&lt;/p&gt;

&lt;p&gt;Container operations have also been substantially expanded. The JIT runtime now supports reading, assignment, deletion, clearing, indexing, and concurrent access across these data types. In addition, Matrix support and non-scalar indexing have been introduced, while numerous container-related crashes, memory issues, and incorrect computation results have been resolved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improved Compatibility for CEP&lt;/strong&gt;&lt;br&gt;
JIT support for classes has been further enhanced to better serve Complex Event Processing (CEP) applications. The new release supports event classes, monitor classes, and helper classes used in CEP, while improving advanced class features such as dictionary/vector member access, nested classes, inter-method calls, and callback function passing. These enhancements improve both execution efficiency and runtime stability in CEP-related workloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Greater Stability and Production Readiness&lt;/strong&gt;&lt;br&gt;
Beyond language enhancements, this release significantly improves the engineering robustness of the JIT engine. Numerous stability issues involving complex data processing, nested assignments, loop execution, classes, and tuples have been resolved, along with multiple memory leaks, persistent memory growth, and abnormal resource utilization issues. Together, these improvements reduce runtime failures and make JIT considerably more reliable for complex business logic running under production-scale data workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;5. Upgraded Dynamic Script Optimization for Faster Nested Functions and Loop Execution&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In quantitative analytics, real-time risk management, and low-latency trading, users frequently write complex scripts containing loops, conditional branches, and function calls. Because these workloads are often difficult to fully vectorize, interpreter overhead can become a significant performance bottleneck.&lt;br&gt;
DolphinDB provides Dynamic Script Optimization, which automatically optimizes interpreted scripts at runtime without requiring any code modifications. Once enabled, it significantly accelerates scripts containing loops, conditional branches, and function calls. In function-intensive workloads, performance improvements of several times have been observed.&lt;/p&gt;

&lt;p&gt;In the new release, this capability has been further refined by changing the runtime optimization scope from process-level to session-level. Configuration parameters continue to define the default behavior, while runtime APIs now affect only the current session without impacting other sessions within the same process. This improvement provides much greater flexibility in multi-user and multi-task environments. For example, users can enable optimization in one session for performance testing while keeping another session unchanged for functional validation, making the feature more practical for production deployment.&lt;/p&gt;

&lt;p&gt;Dynamic Script Optimization is particularly effective for the following scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Deeply nested function calls, such as strategy logic decomposed into multiple reusable functions with frequent invocation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Complex iterative computations, including tick-by-tick or bar-by-bar processing, element-wise window calculations, and recursive state updates.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;High-frequency conditional branching, such as signal generation, chained risk control rules, and sequential trading condition filtering.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Business logic that cannot be easily vectorized, including state-dependent, path-dependent, or multi-stage decision workflows.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For these workloads, Dynamic Script Optimization significantly reduces interpreter overhead and minimizes the performance cost of loops and function calls. Benchmark results show performance improvements of 63.9%–88.8% for function-intensive scripts, up to 8.93× acceleration for deeply nested function calls, and more than 30% performance gains for representative financial factor calculations such as VWAP.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;6. Enhanced High Availability for Streaming Data&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The new release further strengthens the high availability (HA) capabilities for streaming data, covering both HA stream tables and HA MVCC tables to improve production reliability.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;For stream tables, data subscription and delivery are now more stable, subscription management is more reliable, and services recover faster after node status changes. These enhancements also extend to the ORCA streaming platform, improving stream graph continuity and overall data reliability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For MVCC tables, HaMvccTable further enhances the stability of data reading, writing, and synchronization, improves resource management, and ensures data consistency across multiple nodes. With continued optimization, HaMvccTable is now more production-ready and provides stronger protection for mission-critical data.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;7. Improvements to Core Capabilities&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This release also delivers enhancements across the database kernel and data analytics engine.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Database&lt;/strong&gt;&lt;br&gt;
Core database capabilities have been improved across distributed scheduling, storage integration, data lifecycle management, and write control. The new release delivers better task scheduling balance in multi-replica clusters, simplified Amazon S3 integration through the AWS SDK default credential chain, enhanced monthly partition retention and cold storage migration, and more precise write controls to prevent unintended NULL values during complex update operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Analytical Functions&lt;/strong&gt;&lt;br&gt;
The function library has been expanded for quantitative research and real-time analytics. New capabilities include time-based weighted calculations, linear filtering, variable-length sliding windows, and rolling drawdown calculations. Custom aggregation and regression options have also been extended, while text import and JSON serialization have been improved for better interoperability with external systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Core Language&lt;/strong&gt;&lt;br&gt;
The core language continues to evolve with improvements to type definitions, class support, control flow, metaprogramming, SQL semantics, and function metadata. New SQL features include union join (uj), ROLLUP, group-level pagination with context by, enhanced type conversion, and improved identifier compatibility. Function annotations have also been enriched with @desc, parameter types, and return type declarations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stream Processing&lt;/strong&gt;&lt;br&gt;
The streaming framework has been further strengthened with more flexible trigger mechanisms in the time-series engine, refined snapshot generation across multiple time ranges, access control identifiers for stream table queries, and improved management of dynamic metrics in ORCA stream graphs, delivering greater stability for complex streaming workloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plugins &amp;amp; API&lt;/strong&gt;&lt;br&gt;
Plugin installation and Python data upload have been streamlined, improving plugin deployment in restricted network environments. Python integration with the Apache Arrow ecosystem has also been enhanced, enabling smoother interoperability with tools such as Polars.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;8. Enhanced System Operations&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The new release strengthens observability, resource management, and operational efficiency for production environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better Observability&lt;/strong&gt;&lt;br&gt;
System monitoring has been improved with richer metadata and status visibility. Administrators can now query historical background tasks based on permissions, while stream tables, functions, shared variables, and in-memory tables expose additional status information. Automatic DDL generation through getDatabaseDDL and getDBTableDDL also simplifies database migration and schema replication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stronger Security and Resource Control&lt;/strong&gt;&lt;br&gt;
AES-256-CBC encryption has been added for secure string encryption and decryption. License management now supports hardware dongle verification and real-time license status monitoring. User-level task concurrency limits help prevent resource contention, while Catalog and Schema objects now support descriptions for easier management in multi-tenant environments. (Some features are available only in V3.00 and later.)&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;9. Performance and User Experience Improvements&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This release delivers comprehensive improvements in data processing performance, system stability, and usability. Enhancements to query parsing, error reporting, storage scheduling, cache warm-up, transaction management, and Leader stability improve performance, observability, and reliability while reducing resource consumption in storage-compute separation, hot/cold tiering, and large-scale data access scenarios.&lt;/p&gt;

&lt;p&gt;At the storage layer, TSDB introduces the new volumeType configuration for storage volumes. With SSD optimization enabled, level file access paths have been optimized to reduce storage latency and improve throughput for high-frequency read workloads. Benchmark results show an average throughput improvement of approximately 13% for multi-level group aggregation, with gains of up to 35% in specific scenarios. CPU-intensive read workloads achieve throughput improvements of up to 109%.&lt;/p&gt;

&lt;p&gt;Usability has also been enhanced with improvements to object display, timestamp handling, dynamic metric management, field parsing, license queries, matrix indexing, and other commonly used data processing capabilities, providing greater flexibility, ease of use, and compatibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;10. Roadmap&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Storage &amp;amp; Data Ingestion&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Introduce persistent mode for FeatureDB to improve the reliability and reusability of feature data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enhance the read performance of external tables (e.g., Amazon S3) to improve data ingestion efficiency in lakehouse architectures.&lt;br&gt;
&lt;strong&gt;Financial Derivatives Pricing&lt;/strong&gt;&lt;br&gt;
Expand support for pricing complex financial derivatives across three major asset classes:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fixed Income: Embedded-option bonds&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Credit: CLN, CRMW, and CRMA&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;FX: Asian, Barrier, Touch, and Quanto options&lt;br&gt;
Further enhance core pricing models — including LV, SV, SLV, PDE, and Monte Carlo methods, and optimize pricing engine compatibility to provide a unified infrastructure for multi-asset derivatives pricing.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AI Agent Ecosystem&lt;/strong&gt;&lt;br&gt;
Launch a suite of domain-specific AI agents for different use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database Operations Agent&lt;/li&gt;
&lt;li&gt;Plugin Development Agent&lt;/li&gt;
&lt;li&gt;Machine Learning &amp;amp; Data Analytics Agent&lt;/li&gt;
&lt;li&gt;Quantitative Research Agent&lt;/li&gt;
&lt;li&gt;Dlang Programming Assistant with long-context conversation support.&lt;/li&gt;
&lt;li&gt;Release an AI agent evaluation framework X-Lab for measuring and validating agent capabilities.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Query Engine Optimization&lt;/strong&gt;&lt;br&gt;
Continue enhancing the SQL engine by improving standards compliance while significantly boosting execution performance for complex multi-table join workloads.&lt;/p&gt;

&lt;p&gt;Download &lt;a href="https://dolphindb.com/product#downloads-top" rel="noopener noreferrer"&gt;DolphinDB V3.00.6 &amp;amp; V2.00.19&lt;/a&gt; now.&lt;/p&gt;

&lt;p&gt;Thanks for reading! Stay connected with DolphinDB by following us on X &lt;a href="https://dolphindb.com/product#downloads-top" rel="noopener noreferrer"&gt;(@DolphinDB_Inc)&lt;/a&gt; and &lt;a href="https://www.linkedin.com/company/dolphindb/posts/?feedView=all&amp;amp;viewAsMember=true" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; for the latest updates, technical articles, and product news. For more information, visit our &lt;a href="https://dolphindb.com/" rel="noopener noreferrer"&gt;website&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How Crypto Pin Bars Form and How to Detect Them: Lessons from a $19B Liquidation</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Wed, 08 Jul 2026 09:02:42 +0000</pubDate>
      <link>https://dev.to/dolphindb/how-crypto-pin-bars-form-and-how-to-detect-them-lessons-from-a-19b-liquidation-7ec</link>
      <guid>https://dev.to/dolphindb/how-crypto-pin-bars-form-and-how-to-detect-them-lessons-from-a-19b-liquidation-7ec</guid>
      <description>&lt;p&gt;One Candlestick, $19 Billion Wiped Out Overnight&lt;/p&gt;

&lt;p&gt;On October 10, 2025, the U.S. abruptly announced a 100% tariff on all Chinese imports and introduced new export restrictions on critical software. The news shattered an otherwise quiet Friday evening. Bitcoin plunged more than 14% from near its all-time high, while several Altcoins fell by as much as 70% before rebounding [1]. Within just a few hours between October 10 and 11, more than $19 billion in leveraged positions were liquidated, making it one of the largest single-day liquidation events in crypto market history.&lt;/p&gt;

&lt;p&gt;Left behind on the charts: a series of long lower wicks that traders know all too well as pin bars.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9oore6xjfd9rk2x8el3d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9oore6xjfd9rk2x8el3d.png" alt=" " width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is a Pin Bar?
&lt;/h2&gt;

&lt;p&gt;A pin bar is an extreme candlestick pattern characterized by &lt;strong&gt;a very long wick and a relatively small body.&lt;/strong&gt; The price briefly breaks through a key support or resistance level before rapidly reversing, leaving behind a distinctive "needle" on the chart. It serves as the market's fingerprint of a brief loss of equilibrium.&lt;/p&gt;

&lt;p&gt;The concept originates from classical candlestick analysis and can be traced back to Steve Nison's pioneering work on candlestick techniques [2]. Traditional definitions, however, are largely qualitative. To provide a more objective definition, we quantify a pin bar using the following criteria:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The wick is significantly longer than the candlestick body.&lt;/li&gt;
&lt;li&gt;The wick accounts for a large proportion of the candlestick's total price range.&lt;/li&gt;
&lt;li&gt;The overall price range exceeds a minimum absolute or relative threshold to filter out normal bid-ask fluctuations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Do Pin Bars Occur?
&lt;/h2&gt;

&lt;p&gt;Market microstructure offers several classic explanations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Informed Trading&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Large institutions or traders with superior information execute sizable orders during periods of limited liquidity, causing significant price impact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Insufficient Liquidity&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Smaller cryptocurrencies often have shallow order books, allowing a single large order to move the market by several percentage points.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Market Manipulation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some traders intentionally trigger clusters of stop-loss or liquidation orders by forcing prices through key levels before buying back at lower prices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Leverage and Cascading Liquidations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the most common and most destructive mechanism. Highly leveraged long positions tend to concentrate their liquidation levels just below important support prices. Once the market reaches those levels, systems automatically execute market sell orders. The additional selling pressure pushes prices even lower, triggering more liquidations and creating a vicious cycle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pin Bar → Liquidations → Deeper Pin Bar&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The October 10 crash provides a textbook example of this mechanism. Bitcoin was trading near its historical highs, with the market heavily positioned on the long side using excessive leverage. Meanwhile, liquidity had thinned during Friday evening as institutional market makers reduced activity. Under such conditions, a single geopolitical shock was enough to ignite the cascade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reconstructing October 10: What the Data Saw
&lt;/h2&gt;

&lt;p&gt;This analysis is based on real market data collected from Binance and OKX throughout October 2025. Three types of datasets are used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Minute-level candlestick (K-line) data for identifying pin bar patterns.&lt;/li&gt;
&lt;li&gt;Aggregated trade data for calculating VPIN (Volume-Synchronized Probability of Informed Trading).&lt;/li&gt;
&lt;li&gt;Order book depth snapshots for measuring order book imbalance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All timestamps are converted to UTC and stored in the DolphinDB distributed database for analysis.&lt;/p&gt;

&lt;p&gt;We begin by reconstructing the event using the three most direct indicators—pin bar patterns, liquidation records, and trading volume—to understand exactly what unfolded on that day.&lt;/p&gt;

&lt;h3&gt;
  
  
  Identifying Pin Bars
&lt;/h3&gt;

&lt;p&gt;We scanned the 5-minute BTCUSDT candlestick data from Binance to identify pin bars using the following criteria:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wick length &amp;gt; 5 × body length&lt;/li&gt;
&lt;li&gt;Wick length &amp;gt; 50% of the total candlestick range&lt;/li&gt;
&lt;li&gt;Candlestick range &amp;gt; 0.5% of the average price&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core implementation is shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="n"&gt;dataKline&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;close&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;open&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="n"&gt;dataKline&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="n"&gt;amplitude&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;high&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;low&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="n"&gt;dataKline&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;high&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="k"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;open&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;close&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="n"&gt;dataKline&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;lower&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;open&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;close&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;low&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="n"&gt;dataKline&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="n"&gt;avgPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;close&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;Detection&lt;/span&gt; &lt;span class="n"&gt;criteria&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;adjust&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;needed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;pinbar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataKline&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="k"&gt;lower&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="k"&gt;lower&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;amplitude&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="n"&gt;pinbar_upper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataKline&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="k"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="k"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;amplitude&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; 
&lt;span class="n"&gt;pinbar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pinbar_upper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pinbar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pinbar&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;amplitude&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;avgPrice&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt; &lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;eventTime&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the 5-minute BTCUSDT candlestick data from Binance as an example, the detection results are shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh825wq00nqscyobvbjiz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh825wq00nqscyobvbjiz.png" alt=" " width="798" height="119"&gt;&lt;/a&gt;&lt;br&gt;
Example of detected pin bars&lt;/p&gt;

&lt;p&gt;Candlesticks identified as pin bars are typically accompanied by unusually high trading volume compared with the day's average. As shown in the figure, at 2025-10-10 21:20:00 UTC, the price briefly fell to 101,516 USDT, dropping by more than 7,000 USDT from the opening price of 108,888 USDT. It then rebounded rapidly and closed at 107,747 USDT. Trading volume during this interval was approximately an order of magnitude higher than during other periods of the day.&lt;/p&gt;

&lt;p&gt;So, who was selling during this period, and how much was actually sold?&lt;/p&gt;
&lt;h3&gt;
  
  
  Liquidation Data: Reconstructing the Cascade of Forced Liquidations
&lt;/h3&gt;

&lt;p&gt;To answer this question, we aggregated minute-level liquidation data from Binance and OKX for the entire month of October. The data were then summarized by liquidation direction (long vs. short) to reveal the overall liquidation dynamics.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;liqDataForSym&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;loadTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"dfs://CryptocurrencyDay"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;"liquidation"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="n"&gt;liqDataForSym&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="n"&gt;eventTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;temporalAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eventTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"H"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="k"&gt;Convert&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;UTC&lt;/span&gt;
&lt;span class="n"&gt;liqDataForSymDay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;symbolSource&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;liqDataForSym&lt;/span&gt; 
    &lt;span class="k"&gt;group&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;symbolSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eventTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;
&lt;span class="n"&gt;res1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;liqDataForSymDay&lt;/span&gt; &lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;
&lt;span class="n"&gt;liqDataForOneDay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;eventTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;symbolSource&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;liqDataForSym&lt;/span&gt; 
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eventTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;group&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;symbolSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;side&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eventTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;eventTime&lt;/span&gt;
&lt;span class="n"&gt;res2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;liqDataForOneDay&lt;/span&gt; &lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;sum_quantity&lt;/span&gt; &lt;span class="k"&gt;desc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkhyho0tjce05id3pciba.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkhyho0tjce05id3pciba.png" alt=" " width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The results are striking. The total liquidation volume on October 10 far exceeded that of any other day in the month. In terms of liquidation direction, long liquidations (forced sell orders) overwhelmingly outnumbered short liquidations, providing strong evidence of a cascading long squeeze. Intraday, liquidation activity peaked between 21:10 and 22:00, closely matching the period in which pin bars were most frequently detected.&lt;/p&gt;

&lt;p&gt;Using XRPUSDT as an example, the figure below illustrates the relationship between minute-level liquidation volume and price movements.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh2mzdccspa2vn1j4pqcl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh2mzdccspa2vn1j4pqcl.png" alt=" " width="799" height="152"&gt;&lt;/a&gt;&lt;br&gt;
Liquidation Data vs. Price Movement&lt;/p&gt;
&lt;h3&gt;
  
  
  Trading Volume Reveals the Sentiment Turning Point
&lt;/h3&gt;

&lt;p&gt;We further analyzed the intraday trading volume of XRPUSDT and compared it with the corresponding price movement. It is important to note that the aggregated trade data used here includes only regular market orders and &lt;strong&gt;excludes liquidation-related forced orders&lt;/strong&gt;. As a result, it provides a clearer view of genuine market sentiment rather than passive trading triggered by forced liquidations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzqm2k5yvopbee5dflvwm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzqm2k5yvopbee5dflvwm.png" alt=" " width="799" height="147"&gt;&lt;/a&gt;&lt;br&gt;
XRPUSDT tradeQty vs Volatility&lt;/p&gt;

&lt;p&gt;The results show that trading volume surged sharply after &lt;strong&gt;20:50&lt;/strong&gt;, indicating that the shift in market sentiment occurred before the largest price decline. A wave of aggressive sell orders entered the market first, disrupting the supply-demand balance. These active sell orders were then amplified by forced liquidation orders, together driving larger price swings and ultimately producing the sharp wick observed around 21:20.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Signals That Were Already There
&lt;/h2&gt;

&lt;p&gt;The previous analysis reconstructed the full sequence of events during the price wick. For traders, however, stop-loss orders being triggered, positions being liquidated, or even accounts being wiped out often happen within minutes—or even seconds. Post-event analysis offers little practical value. The more important question is whether these events can be anticipated before the wick forms. &lt;/p&gt;

&lt;p&gt;To answer this, we evaluated two market microstructure indicators—VPIN and Order Book Imbalance—using historical data to determine whether the market had already begun signaling elevated risk before the extreme price movement occurred.&lt;/p&gt;
&lt;h3&gt;
  
  
  VPIN: Tracking the Evolution of Order Flow Toxicity
&lt;/h3&gt;

&lt;p&gt;VPIN (Volume-Synchronized Probability of Informed Trading) was introduced by Easley et al. in 2011 [3]. Its key idea is **to measure market activity based on trading volume rather than clock time. **Specifically, the order flow is divided into equal-volume buckets, and the imbalance between aggressive buy orders and aggressive sell orders is calculated for each bucket. A larger imbalance indicates that one side is dominating the market, implying higher order flow toxicity and a greater likelihood of extreme price movements. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzmiihsbhh1hykezess36.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzmiihsbhh1hykezess36.png" alt=" " width="216" height="47"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We calculated the VPIN indicator using aggregated trade data to track the dynamics of order flow toxicity across multiple cryptocurrencies throughout October. &lt;/p&gt;

&lt;p&gt;Parameter settings: The fixed bucket size (V) was determined by dividing the average daily trading volume over the previous 20 trading days by 50 buckets per day. A rolling window of 10 buckets was used to calculate VPIN. The indicator was computed in parallel for BTC, ETH, XRP, and ADA.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;symList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;`BTCUSDT,`&lt;/span&gt;&lt;span class="n"&gt;ETHUSDT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;`XRPUSDT,`&lt;/span&gt;&lt;span class="n"&gt;ADAUSDT&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
&lt;span class="n"&gt;targetSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"Binance-Futures"&lt;/span&gt;
&lt;span class="n"&gt;dataType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"trade"&lt;/span&gt;  &lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="nv"&gt;"trade"&lt;/span&gt; &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="nv"&gt;"minKLine"&lt;/span&gt;
&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;  &lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="k"&gt;first&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;divide&lt;/span&gt; &lt;span class="k"&gt;each&lt;/span&gt; &lt;span class="k"&gt;day&lt;/span&gt; &lt;span class="k"&gt;into&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="n"&gt;buckets&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;calculate&lt;/span&gt; &lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;window&lt;/span&gt; &lt;span class="k"&gt;size&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;Parallel&lt;/span&gt; &lt;span class="n"&gt;computation&lt;/span&gt; &lt;span class="n"&gt;across&lt;/span&gt; &lt;span class="n"&gt;multiple&lt;/span&gt; &lt;span class="n"&gt;trading&lt;/span&gt; &lt;span class="n"&gt;days&lt;/span&gt;
&lt;span class="nb"&gt;Date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;   &lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;UTC&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;
&lt;span class="k"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;peach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;calcVPINForAllSym&lt;/span&gt;&lt;span class="p"&gt;{,&lt;/span&gt;&lt;span class="n"&gt;symList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;targetSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dataType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;unionAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;eventTime&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;Calculate&lt;/span&gt; &lt;span class="n"&gt;volatility&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;correlation&lt;/span&gt; &lt;span class="n"&gt;simultaneously&lt;/span&gt;
&lt;span class="k"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;calcVPINVolCor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The figure below compares VPIN with price volatility.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4l4xemdxc4fisy0c6s1m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4l4xemdxc4fisy0c6s1m.png" alt=" " width="798" height="160"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffabi8q5jv6msxoj9xrzh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffabi8q5jv6msxoj9xrzh.png" alt=" " width="798" height="149"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2x91nyprq83ggmhjb0hg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2x91nyprq83ggmhjb0hg.png" alt=" " width="800" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the result we can see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ADA and ETH: VPIN and price volatility were highly synchronized around October 10. In many cases, sharp increases in VPIN either preceded or coincided with significant price swings, suggesting that VPIN exhibits meaningful early warning characteristics.&lt;/li&gt;
&lt;li&gt;BTC: The relationship between VPIN and price volatility was noticeably weaker, which is consistent with Bitcoin's larger market size and stronger liquidity absorption capacity.&lt;/li&gt;
&lt;li&gt;Cross-asset pattern: The lower the liquidity of an asset, the more sensitive VPIN is as an early warning indicator for extreme market events.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The imbalance in order flow is already evident—but what about order book liquidity? Was market depth deteriorating at the same time?&lt;/p&gt;
&lt;h3&gt;
  
  
  Order Book Imbalance: Detecting the Early Signs of Liquidity Exhaustion
&lt;/h3&gt;

&lt;p&gt;Another indicator that directly reflects market conditions is the &lt;strong&gt;Order Book Depth Ratio&lt;/strong&gt;. It is calculated as the ratio of the cumulative bid size from Level 1 to Level N to the cumulative ask size from Level 1 to Level N.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fydksn7hz9ewinjwlk3l9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fydksn7hz9ewinjwlk3l9.png" alt=" " width="181" height="42"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When this ratio drops sharply, it indicates that buy-side liquidity is rapidly disappearing. Under such conditions, even modest selling pressure can trigger a cascade of price declines [4].&lt;/p&gt;

&lt;p&gt;To validate this hypothesis, we analyzed order book depth snapshots from &lt;strong&gt;October 10, 2025&lt;/strong&gt;. To avoid signal dilution caused by averaging multiple snapshots, we retained only the last snapshot of each minute. We then calculated a normalized order book imbalance metric and compared it with subsequent price volatility.&lt;/p&gt;

&lt;p&gt;The analysis consisted of the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Retrieve order book depth data and calculate the imbalance metric.&lt;/li&gt;
&lt;li&gt;Retain the last order book snapshot of each minute to preserve signal strength.&lt;/li&gt;
&lt;li&gt;Calculate future returns and price volatility over different forward-looking windows.&lt;/li&gt;
&lt;li&gt;Identify depth imbalance spikes using the 90th percentile of depthRatioQtyDiff for each asset as the threshold.&lt;/li&gt;
&lt;li&gt;Compute ampRatio = future volatility following a Spike / future volatility without a Spike to evaluate the predictive power of the indicator.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="n"&gt;CalcDepthRatioModule&lt;/span&gt;
&lt;span class="n"&gt;symList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;`BTCUSDT,`&lt;/span&gt;&lt;span class="n"&gt;XRPUSDT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;`ETHUSDT,`&lt;/span&gt;&lt;span class="n"&gt;ADAUSDT&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;targetSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"Binance-Futures"&lt;/span&gt;
&lt;span class="n"&gt;targetDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;  &lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;supports&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="k"&gt;input&lt;/span&gt;
&lt;span class="n"&gt;futureStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;  &lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="n"&gt;future&lt;/span&gt; &lt;span class="n"&gt;bars&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;amplitude&lt;/span&gt; &lt;span class="k"&gt;window&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="k"&gt;Call&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;depthRatioAnalysis&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="k"&gt;execute&lt;/span&gt;
&lt;span class="k"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;spikeComparisonForSym&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;corrForSym&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;depthRatioAnalysis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;targetSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;targetDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;futureStep&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Using ADAUSDT as an example, the figure below compares the order book imbalance indicator with price movement.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fios9ekyohvnt8ha9bk8i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fios9ekyohvnt8ha9bk8i.png" alt=" " width="798" height="159"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the order book imbalance exceeded the 90th percentile, creating an extreme spike, the average price range over the following five minutes increased significantly across all assets. This suggests that severe order book imbalances are indeed associated with elevated short-term volatility and can serve as an effective early warning signal for flash wicks. &lt;/p&gt;

&lt;p&gt;The order book data used in this case includes the top 20 price levels from Binance and the top 5 price levels from OKX. Since this does not represent the exchanges' complete order books, we also used the difference between the best bid and best ask quantities (bidQty[0] - askQty[0]) as a real-time proxy for market liquidity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;loadTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"dfs://CryptocurrencyTick"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;"depth"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"BTCUSDT"&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;symbolSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sySource&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;eventTime&lt;/span&gt; &lt;span class="k"&gt;between&lt;/span&gt; &lt;span class="n"&gt;startTargetTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;endTargetTime&lt;/span&gt;
&lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="n"&gt;eventTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;temporalAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eventTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"H"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="k"&gt;Convert&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;UTC&lt;/span&gt;
&lt;span class="n"&gt;plotData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;eventTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;symbolSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,(&lt;/span&gt;&lt;span class="n"&gt;bidQty&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;askQty&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;diffQty&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;Difference&lt;/span&gt; &lt;span class="k"&gt;between&lt;/span&gt; &lt;span class="n"&gt;cumulative&lt;/span&gt; &lt;span class="n"&gt;bid&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;ask&lt;/span&gt; &lt;span class="n"&gt;quantities&lt;/span&gt; &lt;span class="n"&gt;across&lt;/span&gt; &lt;span class="n"&gt;multiple&lt;/span&gt; &lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="n"&gt;levels&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;plotData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;eventTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;symbolSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;bidQty&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;askQty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;diffQty&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;
&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plotData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;`diffQty],plotData.eventTime.minute(),"bid-ask spread",extras={multiYAxes: true})
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flmu2lxvjxb52z19xeqin.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flmu2lxvjxb52z19xeqin.png" alt=" " width="800" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As shown in the figure above, the order book exhibited several episodes of significant imbalance throughout the day, with the most pronounced fluctuations occurring between 21:00 and 22:10. This period coincided with the sharpest price movements, indicating severe order flow imbalance and a substantially higher likelihood of flash wick events. &lt;/p&gt;

&lt;h2&gt;
  
  
  Building a Real-Time Risk Monitoring System with VPIN
&lt;/h2&gt;

&lt;p&gt;Identifying the signal is only the first step. During extreme market conditions, traders cannot afford to wait until offline analysis is complete before taking action. To address this, we integrated these indicators into a real-time stream processing pipeline and built a VPIN-based monitoring system that automatically generates alerts whenever abnormal conditions are detected, helping traders reduce exposure or exit positions before market conditions deteriorate further. &lt;/p&gt;

&lt;p&gt;The workflow consists of four steps: &lt;/p&gt;

&lt;p&gt;1. Continuously ingest aggregated trade data and divide the order flow into equal-volume buckets (bucket size = average daily trading volume over the previous 20 trading days ÷ 50 buckets per day). &lt;/p&gt;

&lt;p&gt;2. Calculate the buy-sell imbalance (diffQty) for each bucket in real time. &lt;/p&gt;

&lt;p&gt;3. Compute VPIN using a rolling window of 5 buckets. Trigger an alert whenever VPIN exceeds a predefined threshold (e.g., 0.2). &lt;/p&gt;

&lt;p&gt;4. Push real-time notifications through channels such as WeCom or email to assist traders in making timely decisions. &lt;/p&gt;

&lt;p&gt;We replayed historical market data from October 10, 2025 to simulate the real-time monitoring process. Using  ADAUSDT as an example: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxzxefc88zo80b0qud509.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxzxefc88zo80b0qud509.png" alt=" " width="800" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The results show that VPIN for ADAUSDT rose sharply after 21:00, closely matching the period of extreme price volatility. This demonstrates that the proposed monitoring framework is capable of providing meaningful early warning signals before flash wick events unfold.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Pin bars are not random market noise. From surging trading volume and rising VPIN to the depletion of order book liquidity, every stage leaves measurable traces in the data. The real danger is not the wick itself, but the liquidity vacuum and leveraged pressure that quietly accumulate beforehand—and those warning signs are already embedded in the market data long before the event occurs. &lt;/p&gt;

&lt;p&gt;All analyses in this article were implemented using DolphinDB. From batch historical replay across multiple exchanges to real-time stream processing of VPIN and order book imbalance with automated alerts, the entire workflow runs within a unified framework. The goal is simple: surface critical signals when they matter—not after the market has already moved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;p&gt;[1] Jung Hua Liu.(2025). Crypto “Black Swan” Crash: An Academic Analysis: &lt;a href="https://medium.com/@gwrx2005/the-october-11-2025-crypto-black-swan-crash-an-academic-analysis-db92a3d2ad66" rel="noopener noreferrer"&gt;++The October 11, 2025 Crypto “Black Swan” Crash: An Academic Analysis | by Jung-Hua Liu | Medium++&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;[2] Nison, S. (1991). &lt;em&gt;Japanese Candlestick Charting Techniques&lt;/em&gt;. New York: New York Institute of Finance.&lt;/p&gt;

&lt;p&gt;[3] Torben G. Andersen,Oleg Bondarenko (2014). VPIN and the Flash Crash: Journal of Financial Markets&lt;/p&gt;

&lt;p&gt;[4] How to Read and Interpret Liquidity and Order Book Depth: &lt;a href="https://www.altrady.com/crypto-trading/fundamental-analysis/liquidity-order-book-depth" rel="noopener noreferrer"&gt;++How to Read and Interpret Liquidity and Order Book Depth++&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Thanks for reading! Stay connected with DolphinDB by following us on X &lt;a href="https://x.com/DolphinDB_Inc" rel="noopener noreferrer"&gt;(@DolphinDB_Inc)&lt;/a&gt; and &lt;a href="https://www.linkedin.com/company/dolphindb/posts/?feedView=all&amp;amp;viewAsMember=true" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; for the latest updates, technical articles, and product news. For more information, visit our &lt;a href="https://dolphindb.com/" rel="noopener noreferrer"&gt;website&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cryptocurrency</category>
      <category>fintech</category>
      <category>database</category>
      <category>datascience</category>
    </item>
    <item>
      <title>Introduction to backtesting strategy: Historical data replay in DolphinDB</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Thu, 29 Aug 2024 03:08:33 +0000</pubDate>
      <link>https://dev.to/dolphindb/introduction-to-backtesting-strategy-historical-data-replay-in-dolphindb-1ole</link>
      <guid>https://dev.to/dolphindb/introduction-to-backtesting-strategy-historical-data-replay-in-dolphindb-1ole</guid>
      <description>&lt;p&gt;In DolphinDB, we can import historical data into a stream table in chronological order as “real-time data” so that the same script can be used both for backtesting and real-time trading. Regarding streaming in DolphinDB please refer to &lt;em&gt;&lt;a href="https://github.com/dolphindb/Tutorials_EN/blob/master/streaming_tutorial.md" rel="noopener noreferrer"&gt;DolphinDB Streaming Tutorial&lt;/a&gt;&lt;/em&gt; .&lt;/p&gt;

&lt;p&gt;This article introduces functions replay and replayDS and then demonstrates the process of data replaying.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8p90r0kgn2jvyyo8mbbj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8p90r0kgn2jvyyo8mbbj.png" alt="Image description" width="720" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Functions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;replay&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;replay(inputTables, outputTables, [dateColumn], [timeColumn], [replayRate], [absoluteRate=true], [parallelLevel=1])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Function replay injects data from specified tables or data sources into stream tables.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;‘inputTables’ is a table or a tuple. Each element of the tuple is an unpartitioned table or a data source generated by function replayDS.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;‘outputTables’ is a table or a tuple of tables, or a string or a string vector. The number of elements of outputTables must be the same as the number of elements of inputTables. If it is a vector, it is a list of the names of the shared stream tables where the replayed data of the corresponding tables of inputTables are saved. If it is a tuple, each element is a shared stream table where the replayed data of the corresponding table in inputTables are saved. The schema of each table in outputTables must be identical as the schema of the corresponding table in inputTables.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;‘dateColumn’ and ‘timeColumn’ are strings indicating the date column and time column in inputTables. If neither is specified, the first column of the table is chosen as ‘dateColumn’. If there is a ‘dateColumn’, it must be one of the partitioning columns. If only ‘timeColumn’ is specified, it must be one of the partitioning columns. If information about date and time comes from the same column (e.g., DATETIME, TIMESTAMP), use the same column for both ‘dateColumn’ and ‘timeColumn’. Data are replayed in batches determined by the smallest unit of time in ‘timeColumn’ or ‘dateColumn’ if ‘timeColumn’ is not specified. For examples, if the smallest unit of time in ‘timeColumn’ is second then all data in the same second are replayed in the same batch; if ‘timeColumn’ is not specified, then all data in the same day are replayed in the same batch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;‘replayRate’ is a nonnegative integer indicating the number of rows to be replayed per second. If it is not specified, it means data are replayed at the maximum speed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;‘replayRate’ is an integer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;‘absoluteRate’ is a Boolean value. The default value is true.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Regarding ‘replayRate’ and ‘absoluteRate’:&lt;/p&gt;

&lt;p&gt;(1) If ‘replayRate’ is a positive integer and absoluteRate=true, replay at the speed of ‘replayRate’ rows per second.&lt;/p&gt;

&lt;p&gt;(2) If ‘replayRate’ is a positive integer and absoluteRate=false, replay at ‘replayRate’ times the original speed of the data. For example, if the difference between the maximum and the minimum values of ‘dateColumn’ or ‘timeColumn’ is n seconds, then it takes n/replayRate seconds to finish the replay.&lt;/p&gt;

&lt;p&gt;(3) If ‘replayRate’ is unspecified or negative, replay at the maximum speed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;‘parallelLevel’ is a positive integer. When the size of individual partitions in the data sources is too large relative to memory size, we need to use function replayDS to further divide individual partitions into smaller data sources. 'parallelLevel' indicates the number of threads loading data into memory from these smaller data sources simultaneously. The default value is 1. If 'inputTables' is a table or a tuple of tables, the effective 'parallelLevel' is always 1.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;replayDS&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;replayDS(sqlObj, [dateColumn], [timeColumn], [timeRepartitionSchema])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Function replayDS generates a group of data sources to be used as the inputs of function replay. It splits a SQL query into multiple subqueries based on 'timeRepartitionSchema' with 'timeColumn' within each 'dateColumn' partition.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;‘sqlObj’ is a table or metacode with SQL statements (such as ) indicating the data to be replayed. The table object of “select from” must use a DATE type column as one of the partitioning columns.&lt;/li&gt;
&lt;li&gt;‘dateColumn’ and ‘timeColumn’ are strings indicating the date column and time column. If neither is specified, the first column of the table is chosen as ‘dateColumn’. If there is a ‘dateColumn’, it must be one of the partitioning columns. If only ‘timeColumn’ is specified, it must be one of the partitioning columns. If information about date and time comes from the same column (e.g., DATETIME, TIMESTAMP), use the same column for both ‘dateColumn’ and ‘timeColumn’. Function replayDS and the corresponding function replay must use the same set of 'dateColumn' and 'timeColumn'.&lt;/li&gt;
&lt;li&gt;‘timeRepartitionSchema’ is a TIME or NANOTIME type vector. ‘timeRepartitionSchema’ deliminates multiple data sources on the dimension of ‘timeColumn’ within each ‘dateColumn’ partition. For example, if timeRepartitionSchema=[t1, t2, t3], then there are 4 data sources within a day: [00:00:00.000,t1), [t1,t2), [t2,t3) and [t3,23:59:59.999).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Replay a single in-memory table&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;replay(inputTable, outputTable, `date, `time, 10)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Replay a single table using data sources&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To replay a single table with a large number of rows, we can use function replayDS together with function replay. Function replayDSdeliminates multiple data sources on the dimension of 'timeColumn' within each 'dateColumn' partition. Parameter 'parallelLevel' of functionreplay` specifies the number of threads loading data into memory from these smaller data sources simultaneously. In this example, 'parallelLevel' is set to 2.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpqu7ln2m0ce0760kvx6z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpqu7ln2m0ce0760kvx6z.png" alt="Image description" width="632" height="81"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Replay multiple tables simultaneously using data sources&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To replay multiple tables simultaneously, assign a tuple of these table names to parameter ‘inputTables’ of function replay and specify the output tables. Each of the output tables corresponds to an input table and should have the same schema as the corresponding input table. All input tables should have identical 'dateColumn' and 'timeColumn'.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frizqoi2b3mple9zhnwbz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frizqoi2b3mple9zhnwbz.png" alt="Image description" width="676" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cancel replay&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If function replay was called with submitJob, we can use getRecentJobs to get jobId, then cancel the replay with command cancelJob.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flt7x72fj2svzp22vpcup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flt7x72fj2svzp22vpcup.png" alt="Image description" width="188" height="68"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If function replay was called directly, we can use getConsoleJobs in another GUI session to get jobId, then cancel the replay use command cancelConsoleJob.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzyamotibcztc90oxcxne.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzyamotibcztc90oxcxne.png" alt="Image description" width="272" height="70"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. How to use replayed data
&lt;/h2&gt;

&lt;p&gt;Replayed data are streaming data. We can subscribe to and process the replayed data in the following 3 ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Subscribe in DolphinDB. Write user-defined functions in DolphinDB to process streaming data.&lt;/li&gt;
&lt;li&gt;Subscribe in DolphinDB. To conduct real-time calculations with streaming data, use DolphinDB’s built-in streaming aggregators such as time-series aggregator, cross-sectional aggregator and anomaly detection engine. They are very easy to use and have excellent performance. In section 3.2, we use a cross-sectional aggregator to calculate the intrinsic value of an ETF.&lt;/li&gt;
&lt;li&gt;With third-party client through DolphinDB’s streaming API.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Examples
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Replay level 1 stock quotes to calculate ETF intrinsic value&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this example, we replay the level 1 stock quotes in US stock markets on 2007/08/17, and calculate the intrinsic value of an ETF with the built-in cross-sectional aggregator in DolphinDB. The following are the schema of the input table ‘quotes’ and a preview of the data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpz4be23nmw34q0t50fg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhpz4be23nmw34q0t50fg.png" alt="Image description" width="502" height="58"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F246lj3gxmrk3wv8nge7q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F246lj3gxmrk3wv8nge7q.png" alt="Image description" width="266" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flhogib5rgcwgvpqpj3nh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flhogib5rgcwgvpqpj3nh.png" alt="Image description" width="492" height="49"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd5h65cdv20uh8cwxnfam.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd5h65cdv20uh8cwxnfam.png" alt="Image description" width="720" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(1) To replay a large amount of data, if we load all data into memory first, we may have an out-of-memory problem. We can first use function replayDS and specify parameter 'timeRepartitionSchema' to divide the data into 60 parts based on the column 'time'.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz4otlnpqljomd2t94rlu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz4otlnpqljomd2t94rlu.png" alt="Image description" width="653" height="79"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(2) Define the output stream table ‘outQuotes’.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6zjm8nneex9vs6eqxdy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6zjm8nneex9vs6eqxdy.png" alt="Image description" width="626" height="56"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(3) Define a dictionary for the ETF components weights and function etfVal to calculate ETF intrinsic value. For simplicity we use an ETF with only 6 component stocks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fero0khk6gt73fdhj5v7z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fero0khk6gt73fdhj5v7z.png" alt="Image description" width="454" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(4) Define a streaming aggregator to subscribe to the output stream table ‘outQuotes’. We specify a filtering condition for the subscription that only data with stock symbols of AAPL, IBM, MSFT, NTES, AMZN or GOOG are published to the aggregator. This significantly reduces unnecessary network overhead and data transfer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzp42m35audgh3yvax0u1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzp42m35audgh3yvax0u1.png" alt="Image description" width="619" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(5) Start to replay data at the specified speed of 100,000 rows per second. The streaming aggregator conducts real-time calculation with the replayed data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flt25a1pbxjwm65qfp5kt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flt25a1pbxjwm65qfp5kt.png" alt="Image description" width="658" height="60"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(6) Check ETF intrinsic values&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F07z8ujazkawggtqahlbo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F07z8ujazkawggtqahlbo.png" alt="Image description" width="343" height="39"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe60enjdifpb5m3qy6glk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe60enjdifpb5m3qy6glk.png" alt="Image description" width="284" height="596"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Performance testing
&lt;/h2&gt;

&lt;p&gt;We tested data replaying in DolphinDB on a server with the following configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server: DELL PowerEdge R730xd&lt;/li&gt;
&lt;li&gt;CPU: Intel Xeon(R) CPU E5–2650 v4(24cores, 48 threads, 2.20GHz)&lt;/li&gt;
&lt;li&gt;RAM: 512 GB (32GB × 16, 2666 MHz)&lt;/li&gt;
&lt;li&gt;Harddisk: 17T HDD (1.7T × 10, read speed 222 MB/s, write speed 210 MB/s)&lt;/li&gt;
&lt;li&gt;Network: 10 Gigabit Ethernet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DolphinDB script:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk4htq53ysqwtf1reogvm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk4htq53ysqwtf1reogvm.png" alt="Image description" width="666" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When replaying at maximum speed (parameter ‘replayRate’ is not specified) and the output table is not subscribed, it only takes about 100 seconds to replay 336,305,414 rows of data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/dolphindb/Tutorials_EN/blob/master/historical_data_replay.md?source=post_page-----497e24af596d--------------------------------" rel="noopener noreferrer"&gt;https://github.com/dolphindb/Tutorials_EN/blob/master/historical_data_replay.md?source=post_page-----497e24af596d--------------------------------&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dolphindb</category>
      <category>backtesting</category>
      <category>datareplay</category>
      <category>marketdata</category>
    </item>
    <item>
      <title>Helpful Tools for Quant丨Efficiently Calculate Transaction Costs from Tick Data</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Wed, 28 Aug 2024 01:48:54 +0000</pubDate>
      <link>https://dev.to/dolphindb/helpful-tools-for-quantgun-efficiently-calculate-transaction-costs-from-tick-data-3ggb</link>
      <guid>https://dev.to/dolphindb/helpful-tools-for-quantgun-efficiently-calculate-transaction-costs-from-tick-data-3ggb</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwexqcy83g5pckqqhzopd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwexqcy83g5pckqqhzopd.png" alt="Image description" width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The calculation of transaction costs from tick data often involves two tables: trade and nbbo. As the timestamps of both tables are at nanosecond level, there are virtually no exact match between the timestamps of the two tables.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3bh3f6e3dfzhxohe9os.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3bh3f6e3dfzhxohe9os.png" alt="Image description" width="720" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Therefore, in order to calculate transaction costs, we need to locate the most recent quote before each trade (of the same stock). We may also need to calculate the average quotes within a specific window relative to each trade. &lt;strong&gt;These non-exact joins are frequently used in quant finance, but they are not supported in most databases&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ui0d7icjmkuyozyqw0h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ui0d7icjmkuyozyqw0h.png" alt="Image description" width="720" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This time, DolphinDB provides you with &lt;strong&gt;asof join&lt;/strong&gt; and &lt;strong&gt;window join&lt;/strong&gt; for these scenarios.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3gnixdc2e20m44yospbn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3gnixdc2e20m44yospbn.png" alt="Image description" width="800" height="228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fltkt1tc3wuftgt651qgs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fltkt1tc3wuftgt651qgs.png" alt="Image description" width="720" height="339"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take a look at the following case!&lt;/p&gt;

&lt;p&gt;The data used in this example is the &lt;strong&gt;high-frequency data&lt;/strong&gt; from the &lt;strong&gt;New York Stock Exchange&lt;/strong&gt;, consisting of two tables: trade and nbbo, respectively containing 27 million and 78 million records. The DolphinDB script is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;trade = loadTable("dfs://EQY", "trade")
select count(*) from trade

nbbo = loadTable("dfs://EQY", "nbbo")
select count(*) from nbbo

// asof join
timer TC1 = select sum(Trade_Volume*abs(Trade_Price-(Bid_Price+Offer_Price)/2))/sum(Trade_Volume*Trade_Price)*10000 as TC_aj from aj(trade,nbbo,`Symbol`Time) where Time between 09:30:00.000000000 : 15:59:59.999999999 group by symbol

// window join
timer TC2 = select sum(Trade_Volume*abs(Trade_Price-(Bid_Price+Offer_Price)/2))/sum(Trade_Volume*Trade_Price)*10000 as TC_wj from pwj(trade,nbbo,-100000000:0,&amp;lt;[avg(Offer_Price) as Offer_Price, avg(Bid_Price) as Bid_Price]&amp;gt;,`Symbol`Time) where Time between 09:30:00.000000000 : 15:59:59.999999999 group by symbol

select * from ej(TC1,TC2,`symbol) where symbol in `AAPL`MS`EBAY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just &lt;strong&gt;one line of script&lt;/strong&gt; can implement complex calculation logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// asof join
timer TC1 = select sum(Trade_Volume*abs(Trade_Price-(Bid_Price+Offer_Price)/2))/sum(Trade_Volume*Trade_Price)*10000 as TC_aj from aj(trade,nbbo,`Symbol`Time) where Time between 09:30:00.000000000 : 15:59:59.999999999 group by symbol
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It takes &lt;strong&gt;339 milliseconds&lt;/strong&gt; to complete the calculation of transaction costs with &lt;strong&gt;asof join&lt;/strong&gt;, which is more than &lt;strong&gt;100 times faster than the equivalent calculation in Python pandas&lt;/strong&gt;, and the script to calculate with &lt;strong&gt;window join&lt;/strong&gt; takes &lt;strong&gt;402 milliseconds&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Figb3qeanqsnz0gy8v1jh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Figb3qeanqsnz0gy8v1jh.png" alt="Image description" width="720" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While calculating transaction costs, DolphinDB shows an excellent performance with concise code. For a visual representation of the operation covered in this article, you can take one minute watching this demo!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/2HfTRLDYUrY" rel="noopener noreferrer"&gt;https://youtu.be/2HfTRLDYUrY&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dolphindb</category>
      <category>database</category>
      <category>stockmarket</category>
      <category>hft</category>
    </item>
    <item>
      <title>Mastering Pairwise Correlations Calculation of Securities Through Coding</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Tue, 27 Aug 2024 07:23:22 +0000</pubDate>
      <link>https://dev.to/dolphindb/mastering-pairwise-correlations-calculation-of-securities-through-coding-251l</link>
      <guid>https://dev.to/dolphindb/mastering-pairwise-correlations-calculation-of-securities-through-coding-251l</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Futjhqcguw56snu0kmdsr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Futjhqcguw56snu0kmdsr.png" alt="Image description" width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Calculating pairwise correlations of securities sheds light on the relationship between different securities, assisting in investment decision-making and risk management. In this article, you’re going to learn &lt;strong&gt;how to calculate the pairwise correlations of multiple securities using high frequency data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The tool we use is &lt;a href="https://www.dolphindb.com/alone/alone.php?id=75" rel="noopener noreferrer"&gt;DolphinDB&lt;/a&gt; and the data we use is the &lt;strong&gt;high-frequency quote data&lt;/strong&gt; of US stocks on August 1, 2007. The raw data is 16.1 GB with 380 million records. We will calculate the pairwise correlations of the &lt;strong&gt;most actively traded 500 stocks&lt;/strong&gt; in that day.&lt;/p&gt;

&lt;p&gt;Let’s delve into the code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select count(*) from loadTable("dfs://TAQ", "quotes") where date= 2007.08.01

def getStockCorrelation(dateValue, num){
    quotes = loadTable("dfs://TAQ", "quotes")

    syms = (exec count(*) from quotes where date = dateValue, time between 09:30:00 : 15:59:59, 0&amp;lt;bid, bid&amp;lt;ofr, ofr&amp;lt;bid*1.1 group by Symbol order by count desc).Symbol[0:num]
    priceMatrix = exec avg(bid + ofr)/2.0 as price from quotes where date = dateValue, Symbol in syms, 0&amp;lt;bid, bid&amp;lt;ofr, ofr&amp;lt;bid*1.1, time between 09:30:00 : 15:59:59 pivot by time.minute() as minute, Symbol
    retMatrix = each(def(x):ratios(x)-1, priceMatrix)
    correlationMatrix = corrMatrix(retMatrix[1:,].ffill())

    mostCorrelated = select * from table(correlationMatrix.columnNames() as sym, correlationMatrix).unpivot(`sym, syms).rename!(`sym`corrSym`corr) context by sym having rank(corr,false) between 1:10
    return mostCorrelated
}

dateValue = 2007.08.01
num = 500
timer mostCorrelated = defStockCorrelation(dateValue,num)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we generate a vector of 500 stock tickers with the largest number of quote records.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;syms = (exec count(*) from quotes where date = dateValue, time between 09:30:00 : 15:59:59, 0&amp;lt;bid, bid&amp;lt;ofr, ofr&amp;lt;bid*1.1 group by Symbol order by count desc).Symbol[0:num]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F236bl1skzfylgqirigq0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F236bl1skzfylgqirigq0.png" alt="Image description" width="720" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why we use “exec” statement?&lt;/p&gt;

&lt;p&gt;The syntax of the “exec” statement is the same as the “select” statement except that “&lt;strong&gt;select&lt;/strong&gt;” always returns a &lt;strong&gt;table **whereas “&lt;/strong&gt;exec*&lt;em&gt;” can return a **scalar, vector, matrix or table&lt;/em&gt;*, which is more convenient for certain operations.&lt;/p&gt;

&lt;p&gt;Next, we select the records for these 500 stocks and filter out dirty data, then generate a &lt;strong&gt;minute-level mid-price matrix&lt;/strong&gt; with stock tickers as column labels and minutes as row labels.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;priceMatrix = exec avg(bid + ofr)/2.0 as price from quotes where date = dateValue, Symbol in syms, 0&amp;lt;bid, bid&amp;lt;ofr, ofr&amp;lt;bid*1.1, time between 09:30:00 : 15:59:59 pivot by time.minute() as minute, Symbol
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifwgjd0rukbgd0h8rikr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifwgjd0rukbgd0h8rikr.png" alt="Image description" width="720" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then convert the minute-level mid-price matrix into a &lt;strong&gt;stock return matrix&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;retMatrix = each(def(x):ratios(x)-1, priceMatrix)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fckqdapkfe7qtr7qlweil.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fckqdapkfe7qtr7qlweil.png" alt="Image description" width="720" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Based on the stock return matrix, we calculate the correlation between every two columns to get a &lt;strong&gt;500 by 500 pairwise correlation matrix&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;correlationMatrix = corrMatrix(retMatrix[1:,].ffill())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcqyprjb2j7umpxo3y24.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffcqyprjb2j7umpxo3y24.png" alt="Image description" width="720" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So actually, it only takes 4 lines of code to get the pairwise correlation matrix from the high frequency data in DolphinDB.&lt;/p&gt;

&lt;p&gt;In addition, running the script only takes &lt;strong&gt;2.5 seconds&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57kbsq5atl8e7iylzk4p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57kbsq5atl8e7iylzk4p.png" alt="Image description" width="720" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To verify the accuracy, we get the 3 stocks with the highest correlation with Lehman Brothers from the pairwise correlation matrix. The outputs are Morgan Stanley, Goldman Sachs, and Merrill Lynch, which are all investment bank stocks that are supposed to be highly correlated with Lehman Brothers.&lt;/p&gt;

&lt;p&gt;In this example, DolphinDB demonstrates excellent performance in data analysis of large amounts of data with elegant code and minimal coding efforts. The calculation process can be encapsulated into a function to be used in script as well as through various APIs such as Python, C++, Java, C#, Go, etc.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;addFunctionView(getStockCorrelation)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the video below to try the efficient calculation 👇&lt;br&gt;
&lt;a href="https://youtu.be/kmz5OsoDBs0" rel="noopener noreferrer"&gt;https://youtu.be/kmz5OsoDBs0&lt;/a&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>stockmarket</category>
      <category>coding</category>
      <category>dolphindb</category>
    </item>
    <item>
      <title>Accelerating Data Analysis: Embracing the Efficient Query and Aggregation Calculations</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Mon, 26 Aug 2024 08:18:06 +0000</pubDate>
      <link>https://dev.to/dolphindb/accelerating-data-analysis-embracing-the-efficient-query-and-aggregation-calculations-5ed6</link>
      <guid>https://dev.to/dolphindb/accelerating-data-analysis-embracing-the-efficient-query-and-aggregation-calculations-5ed6</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdfjblv0t0sca60aziezi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdfjblv0t0sca60aziezi.png" alt="Image description" width="720" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Efficient query and calculation capabilities play a pivotal role in handling large-scale datasets, and ensuring timely and accurate data analysis. In this article, we will explore &lt;strong&gt;how DolphinDB’s query and advanced calculation features significantly enhance data processing and analysis workflows through practical code examples&lt;/strong&gt;👇&lt;/p&gt;

&lt;p&gt;In this case, we choose &lt;strong&gt;TSDB engine&lt;/strong&gt; as the DolphinDB storage engine. We use a server with 16 CPU cores, 512 GB of memory, and 4 SSDs. The read speed of each disk is about 400 MB/s.&lt;/p&gt;

&lt;p&gt;The data we use is the US Stock Exchange level 1 quotes of 4 years. The &lt;strong&gt;original data size&lt;/strong&gt; is around &lt;strong&gt;12.75 TB&lt;/strong&gt;. &lt;strong&gt;After compression&lt;/strong&gt;, the disk space occupied by DolphinDB is &lt;strong&gt;2.12 TB&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;All these data are stored in one table in DolphinDB, and there are almost &lt;strong&gt;270 billion records&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;login(`admin, `123456)
pnodeRun(clearAllCache)
quotes = loadTable("dfs://TAQ", "quotes")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ud1i4zf1zo3uo6lwy3p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ud1i4zf1zo3uo6lwy3p.png" alt="Image description" width="720" height="186"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First, let’s do the query.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can get the data of Lehman Brothers at 3:59:59 PM, August 21, 2007 through the code below. It turns out that DolphinDB takes only 7.9 ms to extract one second’s data of a stock from 270 billion (12.75 TB) records.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select count(*) from quotes 

timer x=select symbol, time, bid, ofr from quotes where symbol='LEH', date=2007.08.21 and time=15:59:59
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fypru6fmeys7roynyad6y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fypru6fmeys7roynyad6y.png" alt="Image description" width="720" height="93"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The query result is assigned to the variable x, and the content of x can be shown in the data browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe2qw0hx2ebivvtgaboui.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe2qw0hx2ebivvtgaboui.png" alt="Image description" width="720" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next, let’s calculate the average bid-ask spread of Lehman Brothers in each minute on Aug 31, 2007.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The bid-ask spread is defined as the ask price minus the bid price, and then divided by the average ask price and the bid price, and we also add some filtering conditions to ensure the validity of the data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;timer avgSpread = select avg((ofr-bid)/(ofr+bid)*2) as avgSpread from quotes where date=2007.08.31, symbol=`LEH, time between 09:30:00 : 15:59:59, ofr&amp;gt;bid, ofr&amp;gt;0, bid&amp;gt;0, ofr/bid&amp;lt;1.2 group by minute(time) as minute
plot(avgSpread.avgSpread, avgSpread.minute, "Average bid-ask spread per minute")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the script, and we can see that &lt;strong&gt;execution takes 89 milliseconds&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87vffy2icu42jireuu8r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87vffy2icu42jireuu8r.png" alt="Image description" width="494" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The calculation result is plotted in this graph.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Folz32ed36aaznug0fuwo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Folz32ed36aaznug0fuwo.png" alt="Image description" width="720" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Then we’ll increase the data volume, remove the stock code limitation, and calculate the bid-and-ask price difference per minute.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;timer avgSpread = select avg((ofr-bid)/(ofr+bid)*2) as avgSpread from quotes where date=2007.09.24, time between 09:30:00 : 15:59:59, ofr&amp;gt;bid, bid&amp;gt;0, ofr/bid&amp;lt;1.2 group by minute(time) as minute
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The data of all stocks in the whole market for a whole day is about 10 GB. Run the script, and it takes &lt;strong&gt;2.1 seconds&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0jn8jgc0uwlec2cje6z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0jn8jgc0uwlec2cje6z.png" alt="Image description" width="573" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a well-designed system, the time it takes to finish a query should be linearly related to the amount of days the query consumes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhu5gv361ccd63iztp7i4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhu5gv361ccd63iztp7i4.png" alt="Image description" width="720" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So this time, we performs the same calculation on 3 days.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;timer avgSpread = select avg((ofr-bid)/(ofr+bid)*2) as avgSpread from quotes where date between 2007.09.25 : 2007.09.27, time between 09:30:00 : 15:59:59, ofr&amp;gt;bid, bid&amp;gt;0, ofr/bid&amp;lt;1.2 group by minute(time) as minute
plot(avgSpread.avgSpread, avgSpread.minute, "Average bid-ask spread per minute")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It takes &lt;strong&gt;5.47 seconds&lt;/strong&gt;, about 3 times as long as the previous query, which means that the time consumption is directly proportional to the amount of data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihq25tavlut4sgoxntzx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fihq25tavlut4sgoxntzx.png" alt="Image description" width="587" height="297"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a word, performance of DolphinDB is pretty good for both querying a small amount of data and aggregating calculations with large amounts of data.&lt;/p&gt;

&lt;p&gt;The complete script is displayed as follows 👇👀&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;login(`admin, `123456)
pnodeRun(clearAllCache)
quotes = loadTable("dfs://TAQ", "quotes")

select count(*) from quotes 

timer x=select symbol, time, bid, ofr from quotes where symbol='LEH', date=2007.08.21 and time=15:59:59

timer avgSpread = select avg((ofr-bid)/(ofr+bid)*2) as avgSpread from quotes where date=2007.08.31, symbol=`LEH, time between 09:30:00 : 15:59:59, ofr&amp;gt;bid, ofr&amp;gt;0, bid&amp;gt;0, ofr/bid&amp;lt;1.2 group by minute(time) as minute
plot(avgSpread.avgSpread, avgSpread.minute, "Average bid-ask spread per minute")

timer avgSpread = select avg((ofr-bid)/(ofr+bid)*2) as avgSpread from quotes where date=2007.09.24, time between 09:30:00 : 15:59:59, ofr&amp;gt;bid, bid&amp;gt;0, ofr/bid&amp;lt;1.2 group by minute(time) as minute
plot(avgSpread.avgSpread, avgSpread.minute, "Average bid-ask spread per minute")

timer avgSpread = select avg((ofr-bid)/(ofr+bid)*2) as avgSpread from quotes where date between 2007.09.25 : 2007.09.27, time between 09:30:00 : 15:59:59, ofr&amp;gt;bid, bid&amp;gt;0, ofr/bid&amp;lt;1.2 group by minute(time) as minute
plot(avgSpread.avgSpread, avgSpread.minute, "Average bid-ask spread per minute")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you can follow the video below, experiencing the efficient query and aggregation calculation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/A_XoHP9NboE" rel="noopener noreferrer"&gt;https://youtu.be/A_XoHP9NboE&lt;/a&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>coding</category>
      <category>stockmarket</category>
      <category>dolphindb</category>
    </item>
    <item>
      <title>High Frequency Data Analysis: Converting High-frequency Signals to Discrete Buy/Sell Signals</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Fri, 23 Aug 2024 03:01:17 +0000</pubDate>
      <link>https://dev.to/dolphindb/high-frequency-data-analysis-converting-high-frequency-signals-to-discrete-buysell-signals-46de</link>
      <guid>https://dev.to/dolphindb/high-frequency-data-analysis-converting-high-frequency-signals-to-discrete-buysell-signals-46de</guid>
      <description>&lt;p&gt;In high-frequency trading, we generate high-frequency signals from trade and quote tick data and analyze these signals to identify trading opportunities. This tutorial demonstrates how to convert high-frequency signals into discrete buy/sell/hold signals. Essentially, the problem is to convert an array of floating-point values into an array of only 3 integers: +1 (buy), 0 (hold) and -1 (sell).&lt;/p&gt;

&lt;p&gt;The conversion rules can be quite simple. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;+1 if signal &amp;gt; t1&lt;/li&gt;
&lt;li&gt;-1 if signal &amp;lt; t2&lt;/li&gt;
&lt;li&gt;0 otherwise&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Such conversion rules can be easily implemented in DolphinDB with function &lt;code&gt;iif&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iif(signal &amp;gt; t1, 1, iif(signal &amp;lt;t2, -1, 0))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, to avoid too frequent reversals of trading direction, we usually adopt a more complex set of rules: if a signal is above the t1 threshold, it’s a buy signal (+1) and the subsequent signals remain buy signals until one falls below the t10 threshold. Similarly, if a signal is below the t2 threshold, it is a sell signal (-1) and the subsequent signals remain sell signals until a signal exceeds the t20 threshold. The relationship between the thresholds is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;t1 &amp;gt; t10 &amp;gt; t20 &amp;gt; t2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the above rules, the value of a trade signal is determined not only by the value of the current signal but also by the state of the previous signal. This is a typical example of path dependence, which is commonly considered unsuitable or difficult to be handled by vector operations and thus very slow in scripting languages including DolphinDB.&lt;/p&gt;

&lt;p&gt;In some cases, however, a path dependence problem can be solved with vector operations. The problem above is one such example. The next section describes how to solve it with vector operations.&lt;/p&gt;

&lt;p&gt;First, find out the signals that fall in the ranges of a determined state:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if signal &amp;gt; t1, state=1&lt;/li&gt;
&lt;li&gt;if signal &amp;lt; t2, state=-1&lt;/li&gt;
&lt;li&gt;if t20&amp;lt;= signal &amp;lt;= t10, state=0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The states of the signals in the ranges of [t2,t20] and [t10,t1] are determined by the states of the signals preceding these ranges.&lt;/p&gt;

&lt;p&gt;The DolphinDB script for implementing the above rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;direction = (iif(signal&amp;gt;t1, 1, iif(signal&amp;lt;t2, -1, iif(signal between t20:t10, 0, NULL)))).ffill().nullFill(0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s run a simple test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;t1= 10
t10 = 6
t20 = -6
t2 = -10
signal = 20.12 8.78 4.39 -20.68 -8.49 -6.98 0.7 2.08 8.97 12.41
direction = (iif(signal&amp;gt;t1, 1, iif(signal&amp;lt;t2, -1, iif(signal between t20:t10, 0, NULL)))).ffill().nullFill(0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script would be like this if we use pandas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;t1=60
t10=50
t20=30
t2=20
signal=pd.Series([20.12,8.78,4.39,-20.68,-8.49,-6.98,0.7,2.08,8.97,12.41])
start=time.time()
direction1=(signal.apply(lambda signal: 1 if signal &amp;gt; t1 else(-1 if signal&amp;lt;t2 else(0 if t20 &amp;lt; signal &amp;lt; t10 else np.nan)))).ffill().fillna(0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test below generates a random array of 10 million signals between 0 and 100 to test the performance of DolphinDB and pandas. The test environment setup is as follows:&lt;/p&gt;

&lt;p&gt;CPU: Intel(R) Core(TM) i7–7700 CPU @3.60GHz 3.60 GHz&lt;/p&gt;

&lt;p&gt;Memory: 16 GB&lt;/p&gt;

&lt;p&gt;OS: Windows 10&lt;/p&gt;

&lt;p&gt;The executions take 171.73ms (DolphinDB) and 3.28 seconds (pandas), respectively.&lt;/p&gt;

&lt;p&gt;DolphinDB script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;t1= 10
t10 = 6
t20 = -6
t2 = -10
signal = rand(100.0, 10000000)
direction = (iif(signal&amp;gt;t1, 1, iif(signal&amp;lt;t2, -1, iif(signal between t20:t10, 0, NULL)))).ffill().nullFill(0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;pandas script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import time
t1= 60
t10= 50
t20= 30
t2= 20
signal= pd.Series(np.random.random(10000000) * 100)
start= time.time()
direction1=(signal.apply(lambda signal: 1 if signal &amp;gt; t1 else(-1 if signal&amp;lt;t2 else(0 if t20 &amp;lt; signal &amp;lt; t10 else np.nan)))).ffill().fillna(0)
end= time.time()
print(end- start)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>hft</category>
      <category>dolphindb</category>
      <category>database</category>
      <category>pandas</category>
    </item>
    <item>
      <title>How to Downsample Your Data Efficiently</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Thu, 22 Aug 2024 03:33:09 +0000</pubDate>
      <link>https://dev.to/dolphindb/how-to-downsample-your-data-efficiently-3037</link>
      <guid>https://dev.to/dolphindb/how-to-downsample-your-data-efficiently-3037</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcymt81c4o24l7jtailva.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcymt81c4o24l7jtailva.jpeg" alt="Image description" width="720" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tired of spending countless mintues downsampling your data? Look no further!&lt;/p&gt;

&lt;p&gt;In this article, you’ll learn how to efficiently downsample &lt;strong&gt;6.48 billion high-frequency records&lt;/strong&gt; to &lt;strong&gt;61 million minute-level records&lt;/strong&gt; in only &lt;strong&gt;41 seconds&lt;/strong&gt; in DolphinDB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The basic configuration of the DolphinDB server is:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;16 CPU cores&lt;/li&gt;
&lt;li&gt;256 GB memory&lt;/li&gt;
&lt;li&gt;4 SSDs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A DolphinDB cluster with 4 data nodes is deployed, and each node uses a SSD.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj1mtvw5fvgh8935zv00t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj1mtvw5fvgh8935zv00t.png" alt="Image description" width="720" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The data we use is:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the level 1 quotes on August, 2007 from New York Stock Exchange&lt;/li&gt;
&lt;li&gt;around 272 GB, with 6.48 billion records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Downsampling can be performed with a SQL statement in DolphinDB.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbc0e4xzjvsfriuxpxc1e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbc0e4xzjvsfriuxpxc1e.png" alt="Image description" width="720" height="166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As the SQL query may involve multiple partitions, DolphinDB breaks down the job into several tasks and assigns the tasks to the corresponding data nodes for parallel execution. When all the tasks are completed, the system merges the intermediate results from the nodes to return the final result.&lt;/p&gt;

&lt;p&gt;The script is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;db = database("dfs://TAQ")
quotes = db.loadTable("quotes")
select count(*)  from quotes where date between 2007.08.01 : 2007.08.31

model=select  top 1 symbol,date, minute(time) as minute, bid, ofr from quotes where date=2007.08.01,symbol=`EBAY
if(existsTable("dfs://TAQ", "quotes_minute_sql"))
 db.dropTable("quotes_minute_sql")
db.createPartitionedTable(model, "quotes_minute_sql", `date`symbol)

timer{
 minuteQuotes=select avg(bid) as bid, avg(ofr) as ofr from quotes where data between 2007.08.01 : 2007.08.31 group by symbol,date,bar(time, 60) as minute
 loadTable("dfs://TAQ", "quotes_minute_sql").append!(minuteQuotes)
}

select count(*)  from loadTable("dfs://TAQ", "quotes_minute")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The frequency can be adjusted as needed just by modifying &lt;code&gt;bar(time, 60)&lt;/code&gt;. Here 60 means the data is downsampled to 1-minute interval as the timestamp values have seconds precision.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkh7wtgz4b21dmbs760lx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkh7wtgz4b21dmbs760lx.png" alt="Image description" width="720" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The table “quotes_minute_sql“ is created with &lt;code&gt;createPartitionedTable&lt;/code&gt; and the downsampled result can be appended to this table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmvu1wty8pw3mazy372xb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmvu1wty8pw3mazy372xb.png" alt="Image description" width="659" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can execute the script and visit the web-based user interface to check the resource usage. It’s shown that all CPU cores have participated in the downsampling. On each data node, 15 tasks are running concurrently as data is being read from disk.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1fgp016m4h6226hechgy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1fgp016m4h6226hechgy.png" alt="Image description" width="720" height="192"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we come back to VScode and check the execution status, we find that it only takes &lt;strong&gt;41 seconds&lt;/strong&gt; to complete the data downsampling, which generates 61 million minute-level records.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fks6hvvfshbniuthe54fx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fks6hvvfshbniuthe54fx.png" alt="Image description" width="720" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DolphinDB exhibits outstanding performance in data downsampling due to the following reasons:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Jobs are executed distributedly and resources of different nodes can be utilized at the same time;&lt;/li&gt;
&lt;li&gt;Compression reduces the disk I/O;&lt;/li&gt;
&lt;li&gt;Columnar storage and vectorized computation improve the efficiency of aggregation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To learn detailed operations of data downsampling, take a look at this demo!&lt;br&gt;
&lt;a href="https://youtu.be/0vRuiz1Lf6Y" rel="noopener noreferrer"&gt;https://youtu.be/0vRuiz1Lf6Y&lt;/a&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>dolphindb</category>
      <category>quant</category>
      <category>sql</category>
    </item>
    <item>
      <title>A Simpler Way to Calculate WorldQuant 101 Alphas</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Wed, 21 Aug 2024 02:31:10 +0000</pubDate>
      <link>https://dev.to/dolphindb/a-simpler-way-to-calculate-worldquant-101-alphas-m32</link>
      <guid>https://dev.to/dolphindb/a-simpler-way-to-calculate-worldquant-101-alphas-m32</guid>
      <description>&lt;p&gt;The formulas of &lt;strong&gt;101 quantitative trading alphas&lt;/strong&gt; used by WorldQuant were presented in the paper &lt;em&gt;&lt;a href="https://arxiv.org/ftp/arxiv/papers/1601/1601.00991.pdf" rel="noopener noreferrer"&gt;101 Formulaic Alphas&lt;/a&gt;&lt;/em&gt;. However, some formulas are complex, leading to challenges in calculation.&lt;/p&gt;

&lt;p&gt;Take the calculation formula of &lt;strong&gt;Alpha#98&lt;/strong&gt; for example, and the data we use is the daily data from the New York Stock Exchange.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Kakushadze et al 2015 Alpha #98: 
(rank(decay_linear(correlation(vwap, sum(adv5, 26.4719), 4.58418), 7.18088)) -
rank(decay_linear(Ts_Rank(Ts_ArgMin(correlation(rank(open), rank(adv15), 20.8187), 8.62571), 6.95668), 8.07206)))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This formula involves both &lt;strong&gt;cross-sectional data&lt;/strong&gt; and &lt;strong&gt;time series data&lt;/strong&gt;, and the calculation uses nested functions with up to 6 levels, which is extremely difficult to implement in most systems.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5mwaekfbfi5naxfbmko.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5mwaekfbfi5naxfbmko.png" alt="Image description" width="720" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We can significantly reduce the development cost of complex calculations such as Alpha#98 by using DolphinDB’s built-in functions with panel data (in matrix form).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ovalgklgbxburgg9e2p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ovalgklgbxburgg9e2p.png" alt="Image description" width="720" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11od5mfbwbowrcd91j9n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11od5mfbwbowrcd91j9n.png" alt="Image description" width="720" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why is DolphinDB’s code so concise and elegant? 😲&lt;/p&gt;

&lt;p&gt;On the one hand, using &lt;strong&gt;panel data&lt;/strong&gt; provided by DolphinDB to implement the Alpha#98 factor simplifies the calculation logic and makes the code very precise. Panel data is a matrix that combines cross-sectional data and time-series data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpxzvii7ptp7m0v8pb67p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpxzvii7ptp7m0v8pb67p.png" alt="Image description" width="674" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the other hand, DolphinDB has &lt;strong&gt;more than 1,500 built-in functions&lt;/strong&gt; and many of them are optimized. You can implement all 101 alphas with DolphinDB built-in functions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ful8tbp0tyjpmb7bk25wq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ful8tbp0tyjpmb7bk25wq.png" alt="Image description" width="720" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s take a look at DolphinDB script, which is very similar to the original formulas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def alpha98Panel(vwap, open, vol){
    return tsRank(mavg(mcorr(vwap, msum(mavg(vol, 5), 26), 5), 1..7)) - tsRank(mavg(mrank(9 - mimin(mcorr(tsRank(open), 
    tsRank(mavg(vol, 15)), 21), 9), true, 7), 1..8))
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the script, and it only takes &lt;strong&gt;986ms&lt;/strong&gt; to generate a matrix with 7,162 columns and 252 rows and calculate Alpha#98 with the matrix.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo4vgpkd36u1o6yi7o33a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo4vgpkd36u1o6yi7o33a.png" alt="Image description" width="720" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Furthermore, DolphinDB supports &lt;strong&gt;unified stream and batch processing&lt;/strong&gt;. It provides the &lt;code&gt;streamEngineParser&lt;/code&gt; function to automatically form a pipeline of stream engines to carry out the specified metrics calculation. You can directly use the Alpha#98 function as the &lt;em&gt;metrics&lt;/em&gt;, with no need to modify the script for metrics calculation.&lt;/p&gt;

&lt;p&gt;Click the demo below to get more info! 👇&lt;br&gt;
&lt;a href="https://youtu.be/B0lYBAI_FEc" rel="noopener noreferrer"&gt;https://youtu.be/B0lYBAI_FEc&lt;/a&gt;&lt;/p&gt;

</description>
      <category>pandas</category>
      <category>hft</category>
      <category>database</category>
      <category>quant</category>
    </item>
    <item>
      <title>DolphinDB to Offer Access to ICE Consolidated Feed and Historical Tick Data</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Tue, 20 Aug 2024 07:30:41 +0000</pubDate>
      <link>https://dev.to/dolphindb/dolphindb-to-offer-access-to-ice-consolidated-feed-and-historical-tick-data-5gb3</link>
      <guid>https://dev.to/dolphindb/dolphindb-to-offer-access-to-ice-consolidated-feed-and-historical-tick-data-5gb3</guid>
      <description>&lt;p&gt;In March 2023, DolphinDB, Inc., a leading provider of time-series databases, and Intercontinental Exchange (NYSE: ICE), agreed to offer access to the ICE Consolidated Feed and its historical tick data services on DolphinDB’s high-performance time series database. &lt;strong&gt;This new offering provides a more efficient and reliable workflow for DolphinDB clients to access ICE’s market data for faster deployment of trading strategy research, testing and executions.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;DolphinDB is a high-performance distributed time-series database, which has been widely adopted by brokerage firms, mutual funds, hedge funds, banks, and exchanges. Through this agreement, DolphinDB users can easily access and analyze a diverse range of market data from ICE for crucial insights regarding investment decisions and risk management.&lt;/p&gt;

&lt;p&gt;The ICE Consolidated Feed provides a broad range of real-time and intra-day data and analytics from over 600 markets and third-party data sources. Global organizations including major banks, brokerage firms, and investment management firms utilize the ICE Consolidated Feed to power their pricing and analytics data requirements. Together, ICE and DolphinDB can offer clients an efficient way to manage, process and analyze massive amounts of market data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftz8g10is72mwudvdrfcm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftz8g10is72mwudvdrfcm.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;“We are excited to work with ICE to bring a comprehensive and efficient suite of data solutions to our clients,” said Dr. Davis Zhou, CEO and founder of DolphinDB. “This integration brings together the products and resources of both parties, which transforms and streamlines data acquisition, processing, and analysis throughout the investment research process. We look forward to a collaboration where we bring out the best in each other to accelerate the development in fintech.”&lt;/p&gt;

&lt;p&gt;“We are pleased to work with DolphinDB, and offer more clients access to ICE’s suite of market data offerings, by leveraging a streamlined integration for fast deployment and reliability,” said Magnus Cattan, Head of ICE Fixed Income and Data Services in APAC. “Our data provides a competitive option to DolphinDB’s clients for accessing in-depth cross-asset global market data.”&lt;/p&gt;

&lt;p&gt;Both DolphinDB and ICE are committed to delivering the highest quality solutions for quantitative investment research, and this collaboration further solidifies their shared vision of advancing the industry through collaboration and innovation. This collaboration will leverage DolphinDB’s data storage and analytics capabilities and ICE’s global market data reach &amp;amp; expertise to deliver comprehensive, reliable and timely financial data solutions to global market participants.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;About DolphinDB&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;DolphinDB offers a unified platform for data warehouse, analytics and streaming workloads. At its core it is a high-performance distributed time-series database. With a fully featured programming language, over 1500 built-in functions and a suite of stream computing engines, DolphinDB enables rapid development of high performance applications for mission critical tasks in global trading.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;About Intercontinental Exchange&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Intercontinental Exchange, Inc. (NYSE: ICE) is a Fortune 500 company that designs, builds and operates digital networks to connect people to opportunity. We provide financial technology and data services across major asset classes that offer our customers access to mission-critical workflow tools that increase transparency and operational efficiencies. We operate exchanges, including the New York Stock Exchange, and clearing houses that help people invest, raise capital and manage risk across multiple asset classes. Our comprehensive fixed income data services and execution capabilities provide information, analytics and platforms that help our customers capitalize on opportunities and operate more efficiently. At ICE Mortgage Technology, we are transforming and digitizing the U.S. residential mortgage process, from consumer engagement through loan registration. Together, we transform, streamline and automate industries to connect our customers to opportunity.&lt;/p&gt;

</description>
      <category>dolphindb</category>
      <category>hft</category>
      <category>quant</category>
      <category>database</category>
    </item>
    <item>
      <title>Integrating Crypto Market Data From Multiple Sources</title>
      <dc:creator>DolphinDB</dc:creator>
      <pubDate>Tue, 20 Aug 2024 07:06:39 +0000</pubDate>
      <link>https://dev.to/dolphindb/integrating-crypto-market-data-from-multiple-sources-l34</link>
      <guid>https://dev.to/dolphindb/integrating-crypto-market-data-from-multiple-sources-l34</guid>
      <description>&lt;p&gt;Cryptocurrency trading professionals typically use Python, Java, Rust, or C++ for exchange API access. Single-source data access presents inherent limitations, such as data loss from server downtime, delayed updates, and limited data quality and refresh rates, which can potentially impact trading decisions.&lt;/p&gt;

&lt;p&gt;This article introduces a DolphinDB solution to access market streams (with Binance Exchange as an example) through both WebSocket and REST APIs. Performance test shows that this integrated approach enhances continuous data access and comprehensive coverage and improves data quality by providing more timely and frequent market updates. By implementing higher frequency data refreshes within shorter intervals, traders can achieve superior market responsiveness and make more informed decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Background
&lt;/h2&gt;

&lt;p&gt;Binance offers two ways for accessing the market streams:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;REST API&lt;/strong&gt;: Suitable for retrieving static data but requires polling for real-time updates.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;WebSocket API&lt;/strong&gt;: Designed for accessing real-time market streams. By maintaining a persistent connection, it enables low-latency retrieval of the latest data.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Binance provides multiple &lt;a href="https://developers.binance.com/docs/binance-spot-api-docs/rest-api" rel="noopener noreferrer"&gt;base endpoints&lt;/a&gt;, each with distinct performance and stability characteristics. We recommend that users test the performance and stability of each base endpoints on the designated server in their specific business context and determine the optimal number of data sources and retrieval methods based on their needs. The proposed approach combines the strengths of both methods to enhance performance and stability by subscribing to data on different base URLs using the same account and setting different processing frequencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Implementation Overview
&lt;/h2&gt;

&lt;p&gt;The integrated market data solution comprises the following components.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9lbb43cvi8m4ghv3bel.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9lbb43cvi8m4ghv3bel.png" alt="Image description" width="720" height="1098"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accessing Market Streams from Binance Exchange&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For each cryptocurrency pair, market data is accessed from Binance via both WebSocket API (with subscription frequency at 100 ms) and REST API (with polling interval of 50 ms).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Processing Data from WebSocket API&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Subscribing to the data from WebSocket API, and writing it to a shared stream table.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Persisting the stream table to a DFS partitioned table.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Processing Data from REST API&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Setting up HTTP requests to fetch data from REST API.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Writing the data to a shared stream table.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Persisting the stream table to a DFS partitioned table.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cleaning and Integrating Received Data&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Creating a shared dictionary to maintain the latest timestamp for each trading pair.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Defining rules for filtering data with latest timestamps, and integrating selected streams into a shared stream table.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Upon receiving new data, comparing timestamps of incoming data with those in table to ensure only the latest data is written.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Persisting the stream table to a DFS partitioned table.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Accessing Market Streams
&lt;/h2&gt;

&lt;p&gt;This chapter outlines the steps of accessing market streams from Binance’s WebSocket and REST APIs using DolphinDB WebSocket and HttpClient Plugins.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 Accessing Data From WebSocket API
&lt;/h3&gt;

&lt;p&gt;We establish a WebSocket subscription task using the DolphinDB WebSocket plugin to access real-time market depth data from Binance WebSocket API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;: Install and load the DolphinDB WebSocket plugin. This step can be skipped if the plugin is already loaded.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;installPlugin("WebSocket")
loadPlugin("WebSocket")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;: Create a shared stream table wssDepthST and persist it to disk.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;colNames = `type`eventTime`transactionTime`code`firstId`lastId`finalId`bidPrice`bidQty`askPrice`askQty`currentTime
colTypes = [SYMBOL, TIMESTAMP, TIMESTAMP, SYMBOL, LONG, LONG, LONG, DOUBLE[], DOUBLE[], DOUBLE[], DOUBLE[], TIMESTAMP]
enableTableShareAndPersistence(table=streamTable(10000:0, colNames, colTypes), tableName=`wssDepthST, cacheSize=12000)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;: Define functions for handling market data.&lt;/p&gt;

&lt;p&gt;The market depth data accessed through WebSocket API is formatted as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"e": "depthUpdate",  "E": 1571889248277, "T": 1571889248276, "s": "BTCUSDT", "U": 390497796, "u": 390497878, "pu": 390497794,
 "b": [["7403.89", "0.002"], ["7403.90", "3.906"], ["7404.00", "1.428"] ,["7404.85", "5.239"], ["7405.43","2.562"]],
 "a": [["7405.96","3.340" ], ["7406.63", "4.525"], ["7407.08", "2.475"], ["7407.15", "4.800"], ["7407.20","0.175"]]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following script defines functions for processing the incoming data in DolphinDB.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Define parseDepth for parsing market depth data
def parseDepth(rawData){
    rawDataDict = parseExpr(rawData).eval().data
    if (rawDataDict["e"]=="depthUpdate"){
        // Process bid data
        bTransposed = rawDataDict.b.double().matrix(b).transpose()
        bidPrice = bTransposed[0].enlist()
        bidQty = bTransposed[1].enlist()
        // Process ask data
        aTransposed = rawDataDict.a.double().matrix(a).transpose()
        askPrice = aTransposed[0].enlist()
        askQty = aTransposed[1].enlist()
        // Extract other relevant fields
        type = rawDataDict["e"]
        eventTime = timestamp(rawDataDict["E"])
        transactionTime = timestamp(rawDataDict["T"])
        code = rawDataDict["s"]
        firstId = rawDataDict["U"]
        lastId = rawDataDict["u"]
        finalId = rawDataDict["pu"]
        currentTime = gmtime(now())
        // Construct a table
        return table(typ, eventTime, transactionTime, code, firstId, 
          lastId, finalId, bidPrice, bidQty, askPrice, askQty, currentTime)
    }
}
// WebSocket event handlers
def onOpen(ws){
    writeLog("WebSocket opened to receive data")
}

def onMessage(mutable streamTable, ws, dataTable){
    for (data in dataTable[`msg]){
        res = parseDepth(data)
        streamTable.append!(res)
    }
}

def onError(ws, error){
    writeLog("WebSocket failed to receive data: " + error.string())
}

def onClose(ws, statusCode, msg){
    writeLog("Connection is closed, status code: " + statusCode.string() + ", " + 
      msg.string())
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt;: Create a WebSocket subscription task.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Connect to Binance WebSocket API
url = "wss://fstream.binance.com/stream?streams=btcusdt@depth20@100ms"
config = dict(STRING, ANY)
// Create a WebSocket subscription task
ws = WebSocket::createSubJob(url, onOpen, onMessage{streamTable=wssDepthST}, onError, onClose, "wssDepth", config)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 5&lt;/strong&gt;: Manage the subscription.&lt;/p&gt;

&lt;p&gt;After establishing the subscription, use &lt;code&gt;getSubJobStat&lt;/code&gt; to view the subscription status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Check subscription status
WebSocket::getSubJobStat()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To cancel the subscription, use &lt;code&gt;cancelSubJob&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Cancel subscription
WebSocket::cancelSubJob("wssDepth")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the complete script for Binance WebSocket data access, refer to &lt;a href="https://docs.dolphindb.com/en/Tutorials/integrating_crypto_market_data.html#topic_pzt_dsm_fcc" rel="noopener noreferrer"&gt;Appendix &lt;/a&gt;binanceDepthWS.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 Accessing Data From REST API
&lt;/h3&gt;

&lt;p&gt;We create a task to send periodic HTTP requests through the HttpClient Plugin for accessing the latest market depth data from Binance REST API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;: Install and load the DolphinDB HttpClient plugin. This step can be skipped if the plugin is already loaded.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;installPlugin("httpClient")
loadPlugin("httpClient")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;: Create a shared stream table restDepthST and persist it to disk.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;colNames = `eventTime`transactionTime`code`lastUpdateId`bidPrice`bidQty`askPrice`askQty`currentTime
colTypes = [TIMESTAMP, TIMESTAMP, SYMBOL, LONG, DOUBLE[], DOUBLE[], DOUBLE[], DOUBLE[], TIMESTAMP]
enableTableShareAndPersistence(table=streamTable(100000:0, colNames, colTypes), tableName=`restDepthST, cacheSize=12000)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;: Define functions for handling market data.&lt;/p&gt;

&lt;p&gt;The market depth data accessed through REST API is formatted as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"lastUpdateId": 1027024,"E": 1589436922972,"T": 1589436922959,
 "bids": [["4.00000000", "431.00000000"]],
 "asks": [["4.00000200", "12.00000000"]]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Define a &lt;code&gt;getDepth&lt;/code&gt; function that sends HTTP requests at regular intervals (every 50 ms) to continuously fetch the latest market data. The retrieved data is parsed and ingested into a stream table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Define getDepth to fetch market data
def getDepth(mutable restDepthST, code, baseUrl){
    do{
        try{
        param = dict(string,string)
        param['symbol'] = code;
        res = httpClient::httpGet(baseUrl,param,10000)

        rawDataDict = parseExpr(res.text).eval()
        b = double(rawDataDict.bids)
        bTransposed = matrix(b).transpose()
        bidPrice = bTransposed[0]
        bidQty = bTransposed[1]
        a = double(rawDataDict.asks)
        aTransposed = matrix(a).transpose()
        askPrice = aTransposed[0]
        askQty = aTransposed[1]

        eventTime = timestamp(rawDataDict["E"])
        transactionTime = timestamp(rawDataDict["T"])
        lastUpdateId = long(rawDataDict["lastUpdateId"])
        currentTime = gmtime(now())

        resTable = table(eventTime as eventTime, transactionTime as transactionTime, 
            code as code, lastUpdateId as lastUpdateId, [bidPrice] as bidPrice, 
            [bidQty] as bidQty, [askPrice] as askPrice, [askQty] as askQty, currentTime as currentTime)
        restDepthST.append!(resTable)
        sleep(50) 
        }
        catch(ex){
            print(ex)
            continue
        }
    }while(true)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt;: Set the base endpoint and submit the job with the name &lt;code&gt;getDepth_BTC&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;baseUrl = "https://fapi.binance.com/fapi/v1/depth"
submitJob("getDepth_BTC","getDepth_BTC", getDepth, restDepthST, "btcusdt", baseUrl)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 5&lt;/strong&gt;: Manage submitted jobs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// View recent jobs
getRecentJobs()
// View intermediate job information
getJobMessage("getDepth_BTC")
// Cancel the job
cancelJob('getDepth_BTC')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the complete script for Binance REST data access, refer to Appendix binanceDepthREST.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Integrating Multi-Source Data
&lt;/h2&gt;

&lt;p&gt;The stream tables wssDepthST and restDepthST receive market data from Binance WebSocket and REST APIs. Due to the differences in subscription methods and delays, rules for aligning and cleaning such multi-source market data are required for later data integration. These rules are designed to compare the timestamp of the incoming data with that in table to determine if the data provides the most recent information.&lt;/p&gt;

&lt;p&gt;Specifically, we implement data integration by creating a shared dictionary to store the latest timestamps for each cryptocurrency pair and using a stream table latestDepthST to store the integrated market data. The table subscribes to wssDepthST and restDepthST with handling rules to update records with latest timestamps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;: Create a shared dictionary for maintaining the latest timestamps for each trading pair to track the most recent update time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;latestTs=syncDict(SYMBOL,TIMESTAMP)
latestTs['btcusdt'] = 2024.05.16 06:39:17.513
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;: Set rules for filtering and ingesting data from restDepthST and wssDepthST into table latestDepthST. By extracting data and event times, the rules check if the record has the latest timestamp. If true, the shared dictionary is updated and new records are appended to latestDepthST.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def toMerged(mutable latestTs, routeName, msg){ 
    symbolID = exec code from msg limit 1
    Timets = exec eventTime from msg limit 1
    lts = latestTs[symbolID]
    if(Timets &amp;gt; lts){
        latestTs[symbolID] = Timets
        appendTestTb = select code, eventTime, lastUpdateId, bidPrice, bidQty, askPrice, askQty, currentTime, routeName as dataroute from msg
        objByName("latestDepthST").append!(appendTestTb)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;: Subscribe to wssDepthST and restDepthST and specify the handler with filtering rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;subscribeTable(tableName="restDepthST", actionName="restTolive", offset=-1, handler=toMerged{latestTs, 'restapi'}, msgAsTable=true, batchSize=1, throttle=0.001)
subscribeTable(tableName="wssDepthST", actionName="wssTolive", offset=-1, handler=toMerged{latestTs, 'wssapi'}, msgAsTable=true, batchSize=1, throttle=0.001)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method allows subscriptions to multiple sources to be integrated into one table. Rules should be specified based on the format of the obtained market data.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Performance Test
&lt;/h2&gt;

&lt;p&gt;We test the data coverage of using WebSocket and REST APIs separately and together by evaluating the data volume received within 24 hours. Larger data volume indicates higher refresh rates on average.&lt;/p&gt;

&lt;p&gt;In our test environment, we use two methods to obtain BTCUSDT spot depth data: WebSocket with a 100-ms frequency (maximum available speed) and REST polling with a 50-ms interval. We then filter and insert the latest market data into a table. While the 50-ms update speed is ideal for balancing update frequency and market data acquisition across multiple currency pairs given bandwidth limitations, it’s important to note that the REST polling method actually yields higher data volumes due to its faster 50 ms interval.&lt;/p&gt;

&lt;p&gt;After 24 hours of data acquisition:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;REST polling: 1,383,480&lt;/li&gt;
&lt;li&gt;WebSocket subscription: 644,808&lt;/li&gt;
&lt;li&gt;Duplicate records: 10,020&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The latest market table latestDepthST receives 1,753,067 new records, with the specific composition as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6wqgz2r6s1so42nos3wx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6wqgz2r6s1so42nos3wx.png" alt="Image description" width="448" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the results, we can see that the number of new records added to latestDepthST is greater than the number of records obtained separately through WebSocket and REST. Moreover, a higher proportion of REST data in the table indicates that this multi-source integration approach can increase the frequency and timeliness of data acquisition compared to a single access method. Additionally, since the data obtained from WebSocket and REST APIs are pushed from different servers, this method helps avoid problems caused by a single server failure. This further demonstrates that multi-source market data access can obtain market streams more quickly, reducing the risks associated with network fluctuations and exchange service failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Conclusion
&lt;/h2&gt;

&lt;p&gt;This article introduces a DolphinDB-based solution in multi-source market stream access and integration of cryptocurrency market. By integrating multiple data sources and implementing efficient data cleansing and storage, DolphinDB not only improves the refresh frequency and timeliness of data acquisition but also ensures data continuity and reliability. The test results show that this approach significantly increases the amount of market data obtained and improves the data refresh interval, providing traders with more reliable and timely market data support.&lt;/p&gt;

&lt;p&gt;In summary, DolphinDB’s multi-source market data integration solution significantly enhances the cryptocurrency market data acquisition process, providing traders with a superior framework for strategic trading decisions and activities.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Appendix
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.dolphindb.com/en/Tutorials/data/integrating_crypto_market_data/wssDepthST.csv" rel="noopener noreferrer"&gt;wssDepthST (sample table)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.dolphindb.com/en/Tutorials/script/integrating_crypto_market_data/binanceDepthWS.dos" rel="noopener noreferrer"&gt;binanceDepthWS (script for Section 3.1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.dolphindb.com/en/Tutorials/script/integrating_crypto_market_data/binanceDepthREST.dos" rel="noopener noreferrer"&gt;binanceDepthREST (script for Section 3.2)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.dolphindb.com/en/Tutorials/script/integrating_crypto_market_data/binanceDepthJoint.dos" rel="noopener noreferrer"&gt;binanceDepthJoint (script for Chapter 4)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
  </channel>
</rss>
