镜像构建测试流水线

This commit is contained in:
ai-liuys 2026-02-21 16:49:51 +08:00
commit c20459582d
12 changed files with 207 additions and 0 deletions

16
.drone.yml Normal file
View File

@ -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

8
.idea/.gitignore generated vendored Normal file
View File

@ -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

8
.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/quant-platform.iml" filepath="$PROJECT_DIR$/.idea/quant-platform.iml" />
</modules>
</component>
</project>

9
.idea/quant-platform.iml generated Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

15
Dockerfile Normal file
View File

@ -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"]

0
README.md Normal file
View File

View File

@ -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
1 date price signal nav
2 2026-01-01 145.32 1 10000.00
3 2026-01-02 147.10 1 10000.00
4 2026-01-03 149.87 1 10000.00
5 2026-01-04 151.22 1 10000.00
6 2026-01-05 148.91 -1 10200.51
7 2026-01-06 147.56 -1 10200.51
8 2026-01-07 150.12 1 10200.51
9 2026-01-08 152.33 1 10200.51
10 2026-01-09 153.47 1 10200.51
11 2026-01-10 150.88 -1 10350.23
12 2026-01-11 149.70 -1 10350.23
13 2026-01-12 151.05 1 10350.23
14 2026-01-13 152.78 1 10350.23
15 2026-01-14 155.21 1 10350.23
16 2026-01-15 157.03 1 10350.23
17 2026-01-16 154.77 -1 10500.11
18 2026-01-17 153.42 -1 10500.11
19 2026-01-18 156.12 1 10500.11
20 2026-01-19 158.29 1 10500.11
21 2026-01-20 159.78 1 10500.11

View File

@ -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
1 date price signal nav
2 2026-01-01 72.11 0 10000.00
3 2026-01-02 74.35 1 10000.00
4 2026-01-03 75.90 1 10000.00
5 2026-01-04 73.45 -1 10180.25
6 2026-01-05 72.87 -1 10180.25
7 2026-01-06 74.02 1 10180.25
8 2026-01-07 75.45 1 10180.25
9 2026-01-08 76.11 1 10180.25
10 2026-01-09 74.78 -1 10310.40
11 2026-01-10 73.95 -1 10310.40
12 2026-01-11 75.22 1 10310.40
13 2026-01-12 76.88 1 10310.40
14 2026-01-13 77.34 1 10310.40
15 2026-01-14 78.12 1 10310.40
16 2026-01-15 79.05 1 10310.40
17 2026-01-16 77.88 -1 10450.60
18 2026-01-17 76.77 -1 10450.60
19 2026-01-18 78.11 1 10450.60
20 2026-01-19 79.33 1 10450.60
21 2026-01-20 80.21 1 10450.60

38
strategies/strategy_a.py Normal file
View File

@ -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

38
strategies/strategy_b.py Normal file
View File

@ -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

27
tests/run_backtest.py Normal file
View File

@ -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()