Chinese Equity Screening with RSI and Ten-Day Returns
Summary
This Chinese-language post describes a stock screen combining three filters: RSI below 65, exclusion of the STAR Market, and a positive return over ten days that remains below 35%. It also gives an illustrative implementation outline using historical prices and a 14-period RSI. The stated screening idea is to favor stocks with recent gains while avoiding readings considered too high by the chosen RSI threshold.
The author notes that the screen does not account for company fundamentals and may select volatile stocks. Suggested refinements include adding valuation or profitability measures, incorporating broad-market conditions, and reviewing the thresholds. The code example’s return calculation appears to use the maximum one-day percentage change within the ten-day window rather than the cumulative ten-day return described by the screen. No backtest results are supplied, so the criteria and implementation should not be treated as validated evidence of profitability.
Key ideas
- The screen requires RSI below 65 and excludes STAR Market stocks.
- It describes selecting stocks with positive but capped ten-day returns.
- The example uses a 14-period RSI, but its change calculation may not match the stated ten-day return rule.
- The post identifies missing fundamental and market-regime filters as limitations.
- No performance results are provided to validate the strategy.
Tags
Full text
# Run a Backtest (Rust)
# Run a Backtest (Rust)
Nautilus provides two Rust APIs for backtesting: `BacktestEngine`
(low-level) and `BacktestNode` (high-level with catalog streaming). This
guide covers both.
For background on backtesting concepts, fill models, and matching engine
behavior, see the [Backtesting](../concepts/backtesting/) concept guide.
For project setup and feature flags, see the
[Rust](../concepts/rust.md#project-setup) concept guide.
## Dependencies
Add the following to your `Cargo.toml`. The `streaming` and
`nautilus-persistence` entries are only needed for the high-level
`BacktestNode` API.
```toml
[dependencies]
nautilus-backtest = { version = "0.65", features = ["streaming"] }
nautilus-execution = "0.65"
nautilus-model = { version = "0.65", features = ["test-support"] }
nautilus-persistence = "0.65"
nautilus-trading = { version = "0.65", features = ["examples"] }
ahash = "0.8"
anyhow = "1"
tempfile = "3"
ustr = "1"
```
If you only need the low-level `BacktestEngine`, drop `streaming`,
`nautilus-persistence`, `tempfile`, and `ustr`.
## BacktestEngine (low-level API)
The low-level API gives direct control: you build the engine, add venues and
instruments, load data in memory, register strategies, and run.
### 1. Create the engine
```rust
use nautilus_backtest::{config::BacktestEngineConfig, engine::BacktestEngine};
let mut engine = BacktestEngine::new(BacktestEngineConfig::default())?;
```
### 2. Add a venue
`SimulatedVenueConfig` uses a `bon::Builder`: only required fields must be set,
every other setting falls back to a documented default. `build()` validates the
configuration and returns a `ConfigResult`, so propagate or unwrap it.
```rust
use nautilus_backtest::config::SimulatedVenueConfig;
use nautilus_execution::models::fee::{FeeModelAny, MakerTakerFeeModel};
use nautilus_model::{
enums::{AccountType, BookType, OmsType},
identifiers::Venue,
types::Money,
};
engine.add_venue(
SimulatedVenueConfig::builder()
.venue(Venue::from("SIM"))
.oms_type(OmsType::Hedging)
.account_type(AccountType::Margin)
.book_type(BookType::L1_MBP)
.starting_balances(vec![Money::from("1_000_000 USD")])
.fee_model(FeeModelAny::MakerTaker(MakerTakerFeeModel::zero()).into())
.build()?,
)?;
```
Override any default by chaining setters, e.g. `.reject_stop_orders(false)` or
`.allow_cash_borrowing(true)`.
### 3. Add instruments and data
```rust
use nautilus_model::instruments::{
Instrument, InstrumentAny, stubs::audusd_sim,
};
let instrument = InstrumentAny::CurrencyPair(audusd_sim());
let instrument_id = instrument.id();
engine.add_instrument(&instrument)?;
let quotes = generate_quotes(instrument_id); // Your data loading function
engine.add_data(quotes, None, true, true)?;
```
### 4. Register a strategy and run
```rust
use nautilus_model::types::Quantity;
use nautilus_trading::examples::strategies::EmaCross;
let strategy = EmaCross::new(
instrument_id,
Quantity::from("100000"),
10, // fast EMA period
20, // slow EMA period
);
engine.add_strategy(strategy)?;
engine.run(None, None, None, false)?;
```
### Run the full example
```bash
cargo run -p nautilus-backtest --features examples --example engine-ema-cross
```
Source:
[`crates/backtest/examples/engine_ema_cross.rs`](https://github.com/nautechsystems/nautilus_trader/tree/develop/crates/backtest/examples/engine_ema_cross.rs)
## BacktestNode (high-level API)
The high-level API loads data from a `ParquetDataCatalog` and streams in
configurable chunk sizes. Requires the `streaming` feature on
`nautilus-backtest`.
### 1. Write data to a catalog
```rust
use nautilus_model::instruments::{
Instrument, InstrumentAny, stubs::audusd_sim,
};
use nautilus_persistence::backend::parquet::catalog::ParquetDataCatalog;
use tempfile::TempDir;
let instrument = InstrumentAny::CurrencyPair(audusd_sim());
let instrument_id = instrument.id();
let quotes = generate_quotes(instrument_id);
let temp_dir = TempDir::new()?;
let catalog_path = temp_dir.path().to_str()
.context("temp dir path is not valid UTF-8")?
.to_string();
let catalog = ParquetDataCatalog::new(
temp_dir.path(), None, None, None, None,
);
catalog.write_instruments(vec![instrument])?;
catalog.write_to_parquet("es, None, None, None)?;
```
### 2. Configure the run
```rust
use nautilus_backtest::config::{
BacktestDataConfig, BacktestRunConfig, BacktestVenueConfig, NautilusDataType,
};
use nautilus_model::enums::{AccountType, BookType, OmsType};
let venue_config = BacktestVenueConfig::builder()
.name("SIM")
.oms_type(OmsType::Hedging)
.account_type(AccountType::Margin)
.book_type(BookType::L1_MBP)
.starting_balances(vec!["1_000_000 USD".to_string()])
.build()?;
let data_config = BacktestDataConfig::builder()
.data_type(NautilusDataType::QuoteTick)
.catalog_path(catalog_path)
.instrument_id(instrument_id)
.build()?;
let run_config = BacktestRunConfig::builder()
.id("ema-cross-run".to_string())
.venues(vec![venue_config])
.data(vec![data_config])
.chunk_size(100)
.build()?;
```
### 3. Build, add strategies, and run
```rust
use nautilus_backtest::node::BacktestNode;
use nautilus_model::types::Quantity;
use nautilus_trading::examples::strategies::EmaCross;
let mut node = BacktestNode::new(vec![run_config])?;
node.build()?;
let engine = node.get_engine_mut("ema-cross-run")
.context("engine not found for run config ID")?;
let strategy = EmaCross::new(
instrument_id,
Quantity::from("100000"),
10,
20,
);
engine.add_strategy(strategy)?;
node.run()?;
```
### Run the full example
```bash
cargo run -p nautilus-backtest --features examples,streaming --example node-ema-cross
```
Source:
[`crates/backtest/examples/node_ema_cross.rs`](https://github.com/nautechsystems/nautilus_trader/tree/develop/crates/backtest/examples/node_ema_cross.rs)Shown in full with attribution under the source's licence. Licence: LGPL-3.0
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.