Skip to content

Commit

Permalink
✨ feat: 2.0.0.a77
Browse files Browse the repository at this point in the history
* strategy增加lifecycle
* 保留最后一个回测周期仅供交易使用,不调用`predict`
* Security获取股票列表时,如果不调用`types`,将获取股票列表,调用`types()`不传参数将获取带指数、股票的列表。
  • Loading branch information
aaron-yang-biz committed Nov 9, 2023
1 parent 2d65ac9 commit c2da9a7
Show file tree
Hide file tree
Showing 10 changed files with 1,985 additions and 1,318 deletions.
4 changes: 4 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# History

## 2.0.0-alpha77
* 增加lifecycle
* 保留最后一个回测周期仅供交易使用,不调用`predict`
* Security获取股票列表时,如果不调用`types`,将获取股票列表,调用`types()`不传参数将获取带指数、股票的列表。
## 2.0.0-alpha76
* 增加backtestlog模块,用于输出回测日志时,将时间替换为回测时间
* 增加行情预取功能
Expand Down
98 changes: 98 additions & 0 deletions docs/_static/strategy_lifecycle.drawio
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<mxfile host="app.diagrams.net" modified="2023-11-02T09:04:11.746Z" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/119.0" etag="PswDgAD1aaHq92OMJ1pJ" version="21.8.1" type="device">
<diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">
<mxGraphModel dx="794" dy="769" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="I5vgsRcotXR9VVc6fSss-2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="WIyWlLk6GJQsqaUBKTNV-3" target="I5vgsRcotXR9VVc6fSss-1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-3" value="回测开始" style="rounded=1;whiteSpace=wrap;html=1;fontSize=12;glass=0;strokeWidth=1;shadow=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="160" y="80" width="120" height="40" as="geometry" />
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-21" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="WIyWlLk6GJQsqaUBKTNV-11" target="I5vgsRcotXR9VVc6fSss-0">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-11" value="回测结束" style="rounded=1;whiteSpace=wrap;html=1;fontSize=12;glass=0;strokeWidth=1;shadow=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="160" y="687" width="120" height="40" as="geometry" />
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-0" value="make_report" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="160" y="761" width="120" height="29" as="geometry" />
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-6" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="I5vgsRcotXR9VVc6fSss-1" target="I5vgsRcotXR9VVc6fSss-5">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-1" value="before_start" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="160" y="160" width="120" height="30" as="geometry" />
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-8" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="I5vgsRcotXR9VVc6fSss-5" target="I5vgsRcotXR9VVc6fSss-7">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-5" value="bars loop" style="ellipse;whiteSpace=wrap;html=1;rounded=0;perimeterSpacing=4;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="180" y="257" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-10" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="I5vgsRcotXR9VVc6fSss-7" target="I5vgsRcotXR9VVc6fSss-9">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-14" value="Y" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="I5vgsRcotXR9VVc6fSss-10">
<mxGeometry x="-0.2333" y="2" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-13" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="I5vgsRcotXR9VVc6fSss-7" target="I5vgsRcotXR9VVc6fSss-12">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-15" value="N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="I5vgsRcotXR9VVc6fSss-13">
<mxGeometry x="-0.3" y="-1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-7" value="新交易日?" style="rhombus;whiteSpace=wrap;html=1;rounded=0;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="180" y="360" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-18" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="I5vgsRcotXR9VVc6fSss-9" target="I5vgsRcotXR9VVc6fSss-17">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-9" value="after_trade" style="whiteSpace=wrap;html=1;rounded=0;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="320" y="385" width="120" height="30" as="geometry" />
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-29" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="I5vgsRcotXR9VVc6fSss-12" target="I5vgsRcotXR9VVc6fSss-25">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-12" value="predict" style="whiteSpace=wrap;html=1;rounded=0;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="160" y="480" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-19" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="I5vgsRcotXR9VVc6fSss-17" target="I5vgsRcotXR9VVc6fSss-12">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-17" value="before_trade" style="whiteSpace=wrap;html=1;rounded=0;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="320" y="490" width="120" height="40" as="geometry" />
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-27" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="I5vgsRcotXR9VVc6fSss-25" target="I5vgsRcotXR9VVc6fSss-5">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="60" y="610" />
<mxPoint x="60" y="297" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-28" value="N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="I5vgsRcotXR9VVc6fSss-27">
<mxGeometry x="0.1091" y="1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-32" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="WIyWlLk6GJQsqaUBKTNV-1" source="I5vgsRcotXR9VVc6fSss-25" target="WIyWlLk6GJQsqaUBKTNV-11">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-33" value="Y" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="I5vgsRcotXR9VVc6fSss-32">
<mxGeometry x="-0.3953" y="1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="I5vgsRcotXR9VVc6fSss-25" value="最后bar?" style="rhombus;whiteSpace=wrap;html=1;" vertex="1" parent="WIyWlLk6GJQsqaUBKTNV-1">
<mxGeometry x="180" y="570" width="80" height="80" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
10 changes: 5 additions & 5 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ Board.init('192.168.100.101')

omicron 通过 [strategy](/api/strategy) 来提供策略框架。通过该框架编写的策略,可以在实盘和回测之间无缝转换 -- 根据初始化时传入的服务器不同而自动切换。

omicron 提供了一个简单的 [双均线策略](/api/strategy/#omicron.strategy.sma) 作为策略编写的示范,可结合其源码,以及本文档中的[完整策略示例](/usage/#完整sma回测示例)在notebook中运行查看。
omicron 提供了一个简单的 [双均线策略](/latest/api/strategy/#omicron.strategy.sma) 作为策略编写的示范,可结合其源码,以及本文档中的[完整策略示例](/latest/usage/#完整sma回测示例)在notebook中运行查看。


策略框架提供了回测驱动逻辑及一些基本函数。要编写自己的策略,您需要从基类[BaseStrategy](/api/strategy/#omicron.strategy.base.BaseStrategy)派生出自己的子类,并改写它的`predict`方法来实现调仓换股。
Expand All @@ -284,8 +284,8 @@ omicron 提供了一个简单的 [双均线策略](/api/strategy/#omicron.strate
1. 从此基类派生出一个策略子类,比如sma.py
2. 子类需要重载`predict`方法,根据当前传入的时间帧和帧类型参数,获取数据并进行处理,评估出交易信号。
3. 子类根据交易信号,在`predict`方法里,调用基类的`buy``sell`方法来进行交易
4. 生成策略实例,通过实例调用`backtest`方法来进行回测,该方法将根据策略构建时指定的回测起始时间、终止时间、帧类型,逐帧生成各个时间帧,并调用子类的`predict`方法。如果调用时指定了`portfolio``min_bars`参数,`backtest`还将进行数据预取,并将截止到当前回测帧时的数据传入。
4. 在交易结束时,调用`plot_metrics`方法来获取如下所示的回测指标图
4. 生成策略实例,通过实例调用`backtest`方法来进行回测,该方法将根据策略构建时指定的回测起始时间、终止时间、帧类型,逐帧生成各个时间帧,并调用子类的`predict`方法。如果调用时指定了`prefetch_stocks`参数,`backtest`还将进行数据预取(预取的数据长度由`warmup_peroid`决定),并将截止到当前回测帧时的数据传入。
5. 在交易结束时,调用`plot_metrics`方法来获取如下所示的回测指标图
![](https://images.jieyu.ai/images/2023/05/20230508160012.png?2)

如何派生子类,可以参考[sma][omicron.strategy.sma.SMAStrategy]源代码。
Expand All @@ -301,7 +301,7 @@ sma = SMAStrategy(
frame_type=FrameType.DAY,
)

await sma.backtest(portfolio=["600000.XSHG", min_bars=20])
await sma.backtest(prefetch_stocks=["600000.XSHG", min_bars=20])
```
在回测时,必须要指定`is_backtest=True``start`, `end`参数。
### 3.2. 回测报告
Expand Down Expand Up @@ -346,7 +346,7 @@ await sma.plot_metrics(indicator)

在回测中,可以使用主周期的数据预取,以加快回测速度。工作原理如下:

如果策略在调用`backtest`时传入了`portfolio``min_bars`参数,则`backtest`将会在回测之前,预取从[start - min_bars * frame_type, end]间的portfolio行情数据,并在每次调用`predict`方法时,通过`barss`参数,将[start - min_bars * frame_type, start + i * frame_type]间的数据传给`predict`方法。传入的数据已进行前复权。
如果策略在调用`backtest`时传入了`prefetch_stocks``min_bars`参数,则`backtest`将会在回测之前,预取从[start - min_bars * frame_type, end]间的portfolio行情数据,并在每次调用`predict`方法时,通过`barss`参数,将[start - min_bars * frame_type, start + i * frame_type]间的数据传给`predict`方法。传入的数据已进行前复权。

如果在回测过程中,需要偷看未来数据,可以使用peek方法。

Expand Down
25 changes: 19 additions & 6 deletions omicron/models/security.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ def include_exit(self) -> "Query":
def types(self, types: List[str]) -> "Query":
"""选择类型在`types`中的证券品种
如果不调用此方法,默认选择所有股票类型。
如果调用此方法但不传入参数,默认选择指数+股票
Args:
types: 有效的类型包括: 对股票指数而言是('index', 'stock'),对基金而言则是('etf', 'fjb', 'mmf', 'reits', 'fja', 'fjm', 'lof')
"""
Expand Down Expand Up @@ -219,6 +221,7 @@ async def eval(self) -> List[str]:
return None

results = []
self._type_pattern = self._type_pattern or SecurityType.STOCK.value
for record in records:
if self._type_pattern is not None:
if record["type"] not in self._type_pattern:
Expand All @@ -232,27 +235,37 @@ async def eval(self) -> List[str]:

# 创业板,科创板,ST暂时限定为股票类型
if self._only_cyb:
if record["type"] != "stock" or not (
if record["type"] != SecurityType.STOCK.value or not (
record["code"][:3] in ("300", "301")
):
continue
if self._only_kcb:
if (
record["type"] != "stock"
record["type"] != SecurityType.STOCK.value
or record["code"].startswith("688") is False
):
continue
if self._only_st:
if record["type"] != "stock" or record["alias"].find("ST") == -1:
if (
record["type"] != SecurityType.STOCK.value
or record["alias"].find("ST") == -1
):
continue
if self._exclude_cyb:
if record["type"] == "stock" and record["code"][:3] in ("300", "301"):
if record["type"] == SecurityType.STOCK.value and record["code"][
:3
] in ("300", "301"):
continue
if self._exclude_st:
if record["type"] == "stock" and record["alias"].find("ST") != -1:
if (
record["type"] == SecurityType.STOCK.value
and record["alias"].find("ST") != -1
):
continue
if self._exclude_kcb:
if record["type"] == "stock" and record["code"].startswith("688"):
if record["type"] == SecurityType.STOCK.value and record[
"code"
].startswith("688"):
continue

# 退市暂不限定是否为股票
Expand Down
3 changes: 2 additions & 1 deletion omicron/plotting/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ def __init__(

if indicator is not None:
self.indicator = indicator.join(
pd.Series(index=self.frames, name="frames"), how="right"
pd.Series(index=self.frames, name="frames", dtype=np.float64),
how="right",
)
else:
self.indicator = None
Expand Down
Loading

0 comments on commit c2da9a7

Please sign in to comment.