镜像构建测试流水线
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