From c20459582de3136de268d162f580d210bfd13d5b Mon Sep 17 00:00:00 2001 From: ai-liuys Date: Sat, 21 Feb 2026 16:49:51 +0800 Subject: [PATCH] =?UTF-8?q?=E9=95=9C=E5=83=8F=E6=9E=84=E5=BB=BA=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E6=B5=81=E6=B0=B4=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .drone.yml | 16 ++++++++++++++++ .idea/.gitignore | 8 ++++++++ .idea/modules.xml | 8 ++++++++ .idea/quant-platform.iml | 9 +++++++++ .idea/vcs.xml | 6 ++++++ Dockerfile | 15 +++++++++++++++ README.md | 0 data/strategy_a_results.csv | 21 ++++++++++++++++++++ data/strategy_b_results.csv | 21 ++++++++++++++++++++ strategies/strategy_a.py | 38 +++++++++++++++++++++++++++++++++++++ strategies/strategy_b.py | 38 +++++++++++++++++++++++++++++++++++++ tests/run_backtest.py | 27 ++++++++++++++++++++++++++ 12 files changed, 207 insertions(+) create mode 100644 .drone.yml create mode 100644 .idea/.gitignore create mode 100644 .idea/modules.xml create mode 100644 .idea/quant-platform.iml create mode 100644 .idea/vcs.xml create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 data/strategy_a_results.csv create mode 100644 data/strategy_b_results.csv create mode 100644 strategies/strategy_a.py create mode 100644 strategies/strategy_b.py create mode 100644 tests/run_backtest.py diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..bc02317 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,16 @@ +kind: pipeline +type: docker +name: quant-ci + +steps: + - name: build docker image + image: docker:20.10 + commands: + # 构建镜像,tag 使用 commit sha + - docker build -t registry.memorion.cn/quant/strategy:${DRONE_COMMIT_SHA} . + # 推送镜像到匿名 registry + - docker push registry.memorion.cn/quant/strategy:${DRONE_COMMIT_SHA} + +trigger: + branch: + - main \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..1a78545 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/quant-platform.iml b/.idea/quant-platform.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/quant-platform.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b243722 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +# Python 回测基础镜像 +FROM python:3.11-slim + +WORKDIR /app + +# 安装依赖 +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# 拷贝策略和测试脚本 +COPY strategies/ ./strategies/ +COPY tests/ ./tests/ + +# 默认执行回测脚本 +CMD ["python", "tests/run_backtest.py", "--strategy", "./strategies", "--output", "/backtests"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/data/strategy_a_results.csv b/data/strategy_a_results.csv new file mode 100644 index 0000000..e987912 --- /dev/null +++ b/data/strategy_a_results.csv @@ -0,0 +1,21 @@ +date,price,signal,nav +2026-01-01,145.32,1,10000.00 +2026-01-02,147.10,1,10000.00 +2026-01-03,149.87,1,10000.00 +2026-01-04,151.22,1,10000.00 +2026-01-05,148.91,-1,10200.51 +2026-01-06,147.56,-1,10200.51 +2026-01-07,150.12,1,10200.51 +2026-01-08,152.33,1,10200.51 +2026-01-09,153.47,1,10200.51 +2026-01-10,150.88,-1,10350.23 +2026-01-11,149.70,-1,10350.23 +2026-01-12,151.05,1,10350.23 +2026-01-13,152.78,1,10350.23 +2026-01-14,155.21,1,10350.23 +2026-01-15,157.03,1,10350.23 +2026-01-16,154.77,-1,10500.11 +2026-01-17,153.42,-1,10500.11 +2026-01-18,156.12,1,10500.11 +2026-01-19,158.29,1,10500.11 +2026-01-20,159.78,1,10500.11 \ No newline at end of file diff --git a/data/strategy_b_results.csv b/data/strategy_b_results.csv new file mode 100644 index 0000000..714a60c --- /dev/null +++ b/data/strategy_b_results.csv @@ -0,0 +1,21 @@ +date,price,signal,nav +2026-01-01,72.11,0,10000.00 +2026-01-02,74.35,1,10000.00 +2026-01-03,75.90,1,10000.00 +2026-01-04,73.45,-1,10180.25 +2026-01-05,72.87,-1,10180.25 +2026-01-06,74.02,1,10180.25 +2026-01-07,75.45,1,10180.25 +2026-01-08,76.11,1,10180.25 +2026-01-09,74.78,-1,10310.40 +2026-01-10,73.95,-1,10310.40 +2026-01-11,75.22,1,10310.40 +2026-01-12,76.88,1,10310.40 +2026-01-13,77.34,1,10310.40 +2026-01-14,78.12,1,10310.40 +2026-01-15,79.05,1,10310.40 +2026-01-16,77.88,-1,10450.60 +2026-01-17,76.77,-1,10450.60 +2026-01-18,78.11,1,10450.60 +2026-01-19,79.33,1,10450.60 +2026-01-20,80.21,1,10450.60 \ No newline at end of file diff --git a/strategies/strategy_a.py b/strategies/strategy_a.py new file mode 100644 index 0000000..5a366a1 --- /dev/null +++ b/strategies/strategy_a.py @@ -0,0 +1,38 @@ +import pandas as pd +import numpy as np + +def run(): + """ + 简单均线策略示例: + - 买入条件:收盘价 > 5 日均线 + - 卖出条件:收盘价 < 5 日均线 + """ + # 模拟历史行情数据 + dates = pd.date_range(start='2026-01-01', periods=20) + prices = pd.Series(np.random.uniform(100, 200, size=20), index=dates) + + # 计算 5 日均线 + ma5 = prices.rolling(window=5).mean() + + # 策略信号:1 买入,-1 卖出 + signal = (prices > ma5).astype(int) - (prices < ma5).astype(int) + + # 简单净值计算 + capital = 10000 + position = 0 + nav = [] + for i in range(len(prices)): + if signal[i] == 1: # 买入 + position = capital / prices[i] + elif signal[i] == -1: # 卖出 + capital = position * prices[i] + position = 0 + nav.append(capital + position * prices[i]) + + result = pd.DataFrame({ + 'date': dates, + 'price': prices, + 'signal': signal, + 'nav': nav + }) + return result \ No newline at end of file diff --git a/strategies/strategy_b.py b/strategies/strategy_b.py new file mode 100644 index 0000000..64d051a --- /dev/null +++ b/strategies/strategy_b.py @@ -0,0 +1,38 @@ +import pandas as pd +import numpy as np + +def run(): + """ + 简单动量策略示例: + - 买入条件:过去 3 日涨幅 > 2% + - 卖出条件:过去 3 日涨幅 < -2% + """ + # 模拟历史行情数据 + dates = pd.date_range(start='2026-01-01', periods=20) + prices = pd.Series(np.random.uniform(50, 150, size=20), index=dates) + + # 计算过去 3 日涨幅 + returns = prices.pct_change(3).fillna(0) + + # 策略信号 + signal = (returns > 0.02).astype(int) - (returns < -0.02).astype(int) + + # 简单净值计算 + capital = 10000 + position = 0 + nav = [] + for i in range(len(prices)): + if signal[i] == 1: + position = capital / prices[i] + elif signal[i] == -1: + capital = position * prices[i] + position = 0 + nav.append(capital + position * prices[i]) + + result = pd.DataFrame({ + 'date': dates, + 'price': prices, + 'signal': signal, + 'nav': nav + }) + return result \ No newline at end of file diff --git a/tests/run_backtest.py b/tests/run_backtest.py new file mode 100644 index 0000000..d97be95 --- /dev/null +++ b/tests/run_backtest.py @@ -0,0 +1,27 @@ +import os +import argparse +import importlib.util +import pandas as pd + +def run_strategy(file_path, output_dir): + spec = importlib.util.spec_from_file_location("strategy", file_path) + strategy = importlib.util.module_from_spec(spec) + spec.loader.exec_module(strategy) + results = strategy.run() + output_file = os.path.join(output_dir, os.path.basename(file_path).replace('.py', '_results.csv')) + results.to_csv(output_file, index=False) + print(f"Backtest results saved to {output_file}") + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--strategy", required=True) + parser.add_argument("--output", required=True) + args = parser.parse_args() + + os.makedirs(args.output, exist_ok=True) + for file in os.listdir(args.strategy): + if file.endswith(".py"): + run_strategy(os.path.join(args.strategy, file), args.output) + +if __name__ == "__main__": + main() \ No newline at end of file