镜像构建测试流水线
This commit is contained in:
commit
c20459582d
16
.drone.yml
Normal file
16
.drone.yml
Normal 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
8
.idea/.gitignore
generated
vendored
Normal 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
8
.idea/modules.xml
generated
Normal 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
9
.idea/quant-platform.iml
generated
Normal 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
6
.idea/vcs.xml
generated
Normal 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
15
Dockerfile
Normal 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"]
|
||||
21
data/strategy_a_results.csv
Normal file
21
data/strategy_a_results.csv
Normal 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
|
||||
|
21
data/strategy_b_results.csv
Normal file
21
data/strategy_b_results.csv
Normal 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
|
||||
|
38
strategies/strategy_a.py
Normal file
38
strategies/strategy_a.py
Normal 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
38
strategies/strategy_b.py
Normal 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
27
tests/run_backtest.py
Normal 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()
|
||||
Loading…
Reference in New Issue
Block a user