Merge pull request #1765 from vnpy/dev

Dev
This commit is contained in:
vn.py 2019-05-30 15:20:59 +08:00 committed by GitHub
commit 6311bb510e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 745 additions and 40 deletions

156
docs/_static/custom.css vendored Normal file
View File

@ -0,0 +1,156 @@
@import url("vendor.css");
html {
position: relative;
min-height: 100%;
}
body {
/* Margin bottom by footer height */
margin-bottom: 60px;
/*padding-top: 50px; */ /* uncomment if navbar should be fixed */
}
body,
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: "Lato","Helvetica Neue",Helvetica,Arial,sans-serif;
}
h1 {
font-size: 42px;
font-weight: 500;
}
h2 {
font-weight: 300;
font-size: 34px;
}
ul, ol {
margin: 0 0 10px 20px;
padding: 0;
}
code {
padding: 0;
}
/* -- sidebar --------------------------------------------------------------- */
.page-sidebar {
margin-top: 25px;
border: 1px solid #cad7e1;
background-color: #FFF;
}
.search > input[type="text"] {
width: 165px;
}
/* dirty but works ¯\_(ツ)_/¯ */
@media (max-width:1200px) and (min-width:768px) {
.search > input[type="text"] {
width: 100px;
}
}
.page-sidebarwrapper {
padding: 10px;
line-height: 1.3;
}
.page-sidebarwrapper ul li a {
display: block;
padding: 3px 0;
}
a.reference {
border: none;
text-decoration: none;
}
a.reference:hover {
text-decoration: underline;
border: none;
}
a.reference:active {
text-decoration: underline;
border: none;
}
a:visited {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
a:active {
text-decoration: underline;
}
img.screenshot {
width: 100%;
box-shadow: 0 4px 16px rgba(85, 85, 85, 0.8);
border-radius: 6px;
margin: 15px 0;
}
.page-content {
padding-top: 0px;
margin-top: 25px;
margin-bottom: 20px;
}
.paper {
background-color: #FFF;
padding: 20px;
border: 1px solid #cad7e1;
position: relative;
}
.paper:before {
content: "";
position: absolute;
top: -1px;
right: -1px;
border-width: 0 40px 40px 0;
border-style: solid;
border-color: #ccc #F6F9FC;
z-index: 3;
}
footer {
position: absolute;
bottom: 0;
width: 100%;
/* Set the fixed height of the footer here */
height: 60px;
padding: 20px 0;
background-color: #333333;
}
.copyright,
.made-in,
.powered-by {
margin: 0;
}
pre {
display: block;
margin: 0 0 12px;
line-height: 1.42857;
word-break: break-all;
word-wrap: break-word;
color: #333;
background-color: #f5f5f5;
border: 1px solid #cad7e1;
border-radius: 0;
}

9
docs/_static/vendor.css vendored Normal file

File diff suppressed because one or more lines are too long

BIN
docs/_static/vnpy.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

140
docs/_templates/layout.html vendored Normal file
View File

@ -0,0 +1,140 @@
{% extends 'basic/layout.html' %}
{%- block extrahead %}
{{ super() }}
<link rel="stylesheet" href="{{ pathto('_static/custom.css', 1) }}" type="text/css" />
{% if theme_touch_icon %}
<link rel="apple-touch-icon" href="{{ pathto('_static/' ~ theme_touch_icon, 1) }}" />
{% endif %}
{% if theme_canonical_url %}
<link rel="canonical" href="{{ theme_canonical_url }}{{ pagename }}.html"/>
{% endif %}
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
{% endblock %}
{# Disable base theme's top+bottom related navs; we have our own in sidebar #}
{%- block relbar1 %}{% endblock %}
{%- block relbar2 %}{% endblock %}
{# Nav should appear before content, not after #}
{%- block content %}
<nav class="navbar navbar-default navbar-fixed-top topnav" role="navigation">
<div class="container topnav">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#topnav-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand topnav" href="https://www.vnpy.com/">vn.py — By Traders, For Traders</a>
</div>
<div class="collapse navbar-collapse" id="topnav-collapse">
<ul class="nav navbar-nav navbar-right">
<li>
<a href="/">主页</a>
</li>
<li>
<a href="/forum/">社区</a>
</li>
<li>
<a href="/docs/">文档</a>
</li>
<li>
<a href="https://www.github.com/vnpy/vnpy" target="_blank">Github</a>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container -->
</nav>
{%- macro sidebar() %}
{%- if render_sidebar %}
<div class="page-sidebar" role="navigation" aria-label="main navigation">
<div class="page-sidebarwrapper">
{%- block sidebarlogo %}
{%- if logo %}
<p class="logo">
<a href="{{ pathto(master_doc) }}"><img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/></a>
</p>
{%- endif %}
{%- endblock %}
{%- if sidebars != None %}
{#- new style sidebar: explicitly include/exclude templates #}
{%- for sidebartemplate in sidebars %}
{%- include sidebartemplate %}
{%- endfor %}
{%- endif %}
</div>
</div>
{%- endif %}
{%- endmacro %}
{%- if theme_fixed_sidebar|lower == 'true' %}
<div class="container">
{% if render_sidebar %}
<div class="col-xs-12 col-sm-4 col-md-4 col-lg-3">
{{ sidebar() }}
</div>
{% endif %}
{%- block document %}
<div class="col-xs-12 col-sm-8 col-md-8 col-lg-9 page-content" role="main">
<div class="paper">
{% block body %} {% endblock %}
</div>
</div>
{%- endblock %}
<div class="clearer"></div>
</div>
{%- else %}
{{ super() }}
{%- endif %}
{%- endblock %}
{%- block footer %}
<footer class="flaskbb-footer">
<div class="container">
<div class="row">
<div class="col-xs-4 col-sm-4 col-md-4 col-lg-4">
<p class="copyright text-muted small pull-left">
© 2015 -
<!--<script type="text/javascript">document.write(new Date().getFullYear());</script>-->
2019
&nbsp;&nbsp;&nbsp;&nbsp;
vn.py Team
</p>
</div>
<div class="col-xs-4 col-sm-4 col-md-4 col-lg-4">
<p class="made-in text-muted small" style="text-align: center">
By Traders, For Traders
</p>
</div>
<div class="col-xs-4 col-sm-4 col-md-4 col-lg-4">
<p class="powered-by text-muted small pull-right">
powered by <a href="https://flaskbb.org">FlaskBB</a>
</p>
</div>
</div>
</div>
</footer>
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
{% if theme_analytics_id %}
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', '{{ theme_analytics_id }}']);
_gaq.push(['_setDomainName', 'none']);
_gaq.push(['_setAllowLinker', true]);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
{% endif %}
{%- endblock %}

22
docs/_templates/relations.html vendored Normal file
View File

@ -0,0 +1,22 @@
{# Changes to the original: capitalized 'Documentation Overview' #}
<div class="relations">
<h3>Related Topics</h3>
<ul>
<li><a href="{{ pathto(master_doc) }}">Documentation Overview</a><ul>
{%- for parent in parents %}
<li><a href="{{ parent.link|e }}">{{ parent.title }}</a><ul>
{%- endfor %}
{%- if prev %}
<li>Previous: <a href="{{ prev.link|e }}" title="{{ _('previous chapter')
}}">{{ prev.title }}</a></li>
{%- endif %}
{%- if next %}
<li>Next: <a href="{{ next.link|e }}" title="{{ _('next chapter')
}}">{{ next.title }}</a></li>
{%- endif %}
{%- for parent in parents %}
</ul></li>
{%- endfor %}
</ul></li>
</ul>
</div>

13
docs/_templates/searchbox.html vendored Normal file
View File

@ -0,0 +1,13 @@
{# Changes to the original: capitalized 'Quick Search' #}
{%- if pagename != "search" and builder != "singlehtml" %}
<div id="searchbox" style="display: none" role="search">
<h3>{{ _('Quick Search') }}</h3>
<form class="search" action="{{ pathto('search') }}" method="get">
<input type="text" name="q" />
<input type="submit" value="{{ _('Go') }}" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
{%- endif %}

6
docs/_templates/sidebarintro.html vendored Normal file
View File

@ -0,0 +1,6 @@
<h3>Useful Links</h3>
<ul>
<li><a href="https://flaskbb.org">FlaskBB Website</a></li>
<li><a href="https://forums.flaskbb.org">FlaskBB Forums</a></li>
<li><a href="https://github.com/sh4nks/flaskbb">FlaskBB @ GitHub</a></li>
</ul>

View File

@ -24,9 +24,9 @@ copyright = '2019, vn.py Team'
author = 'vn.py Team'
# The short X.Y version
version = '2.0'
version = '2.0.3'
# The full version, including alpha/beta/rc tags
release = '2.0-DEV'
release = '2.0.3'
# -- General configuration ---------------------------------------------------
@ -38,7 +38,9 @@ release = '2.0-DEV'
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'recommonmark'
'sphinx.ext.autodoc',
'sphinx.ext.intersphinx',
'recommonmark',
]
# Add any paths that contain templates here, relative to this directory.
@ -72,7 +74,7 @@ locale_dirs = ["locale/"]
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = None
pygments_style = 'sphinx'
# -- Options for HTML output -------------------------------------------------
@ -85,8 +87,20 @@ html_theme = 'alabaster'
# documentation.
#
html_theme_options = {
'logo': 'vnpy.ico',
"base_bg": "inherit",
"narrow_sidebar_bg": "inherit",
'github_banner': True,
'github_user': 'vnpy',
'github_repo': 'vnpy',
'github_type': 'star',
'description': (r"<div class='col-md-12'>"
r"<strong>VN.PY</strong>"
r"</div>"
r"<br/>"
r"By Traders, For Traders"),
'fixed_sidebar': True,
'show_related': True
}
# Add any paths that contain custom static files (such as style sheets) here,
@ -102,7 +116,21 @@ html_static_path = ['_static']
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
#
# html_sidebars = {}
html_sidebars = {
'index': [
'about.html',
'sidebarintro.html',
'sourcelink.html',
'searchbox.html'
],
'**': [
'about.html',
'localtoc.html',
'relations.html',
'sourcelink.html',
'searchbox.html'
]
}
# -- Options for HTMLHelp output ---------------------------------------------
@ -158,6 +186,11 @@ texinfo_documents = [
'Miscellaneous'),
]
# -- Options for intersphinx output ----------------------------------------------
intersphinx_mapping = {
#'python': ('https://docs.python.org/3/', None),
}
# -- Options for Epub output -------------------------------------------------
# Bibliographic Dublin Core info.
@ -171,6 +204,11 @@ epub_title = project
# A unique identification for the text.
#
# epub_uid = ''
# A list of files that should not be packed into the epub file.
epub_exclude_files = ['search.html']
# -- Options for Autodoc -------------------------------------------------
autodoc_default_options = {
'member-order': 'bysource',
'undoc-members': True,
}

View File

@ -2,6 +2,7 @@
Widget for algo trading.
"""
import csv
from functools import partial
from datetime import datetime
@ -68,6 +69,12 @@ class AlgoWidget(QtWidgets.QWidget):
start_algo_button.clicked.connect(self.start_algo)
form.addRow(start_algo_button)
load_csv_button = QtWidgets.QPushButton("CSV启动")
load_csv_button.clicked.connect(self.load_csv)
form.addRow(load_csv_button)
form.addRow(QtWidgets.QLabel(""))
form.addRow(QtWidgets.QLabel(""))
form.addRow(QtWidgets.QLabel(""))
self.setting_name_line = QtWidgets.QLineEdit()
@ -77,8 +84,79 @@ class AlgoWidget(QtWidgets.QWidget):
save_setting_button.clicked.connect(self.save_setting)
form.addRow(save_setting_button)
for button in [
start_algo_button,
load_csv_button,
save_setting_button
]:
button.setFixedHeight(button.sizeHint().height() * 2)
self.setLayout(form)
def load_csv(self):
""""""
# Get csv file path from dialog
path, type_ = QtWidgets.QFileDialog.getOpenFileName(
self,
u"加载算法配置",
"",
"CSV(*.csv)"
)
if not path:
return
# Create csv DictReader
with open(path, "r") as f:
buf = [line for line in f]
reader = csv.DictReader(buf)
# Check whether the csv file has all fields
for field_name in self.widgets.keys():
if field_name not in reader.fieldnames:
QtWidgets.QMessageBox.warning(
self,
"字段缺失",
f"CSV文件缺失算法{self.template_name}所需字段{field_name}"
)
return
# Read setting
settings = []
for d in reader:
# Initialize algo setting with template name
setting = {
"template_name": self.template_name
}
# Read field from each dict of csv line
for field_name, tp in self.widgets.items():
field_type = tp[-1]
field_text = d[field_name]
if field_type == list:
field_value = field_text
else:
try:
field_value = field_type(field_text)
except ValueError:
QtWidgets.QMessageBox.warning(
self,
"参数错误",
f"{field_name}参数类型应为{field_type},请检查!"
)
return
setting[field_name] = field_value
# Add setting into list
settings.append(setting)
# Only start algos if no exception/error occured
for setting in settings:
self.algo_engine.start_algo(setting)
def get_setting(self):
"""
Get setting value from line edits.

View File

@ -339,7 +339,7 @@ class BacktesterEngine(BaseEngine):
self.write_log(f"{vt_symbol}-{interval}开始下载历史数据")
symbol, exchange = extract_vt_symbol(vt_symbol)
req = HistoryRequest(
symbol=symbol,
exchange=exchange,
@ -390,3 +390,15 @@ class BacktesterEngine(BaseEngine):
self.thread.start()
return True
def get_all_trades(self):
""""""
return self.backtesting_engine.get_all_trades()
def get_all_orders(self):
""""""
return self.backtesting_engine.get_all_orders()
def get_all_daily_results(self):
""""""
return self.backtesting_engine.get_all_daily_results()

View File

@ -12,6 +12,7 @@ from ..engine import (
from vnpy.trader.constant import Interval
from vnpy.trader.engine import MainEngine
from vnpy.trader.ui import QtCore, QtWidgets, QtGui
from vnpy.trader.ui.widget import BaseMonitor, BaseCell, DirectionCell, EnumCell
from vnpy.event import Event, EventEngine
@ -95,11 +96,26 @@ class BacktesterManager(QtWidgets.QWidget):
downloading_button = QtWidgets.QPushButton("下载数据")
downloading_button.clicked.connect(self.start_downloading)
self.order_button = QtWidgets.QPushButton("委托记录")
self.order_button.clicked.connect(self.show_backtesting_orders)
self.order_button.setEnabled(False)
self.trade_button = QtWidgets.QPushButton("成交记录")
self.trade_button.clicked.connect(self.show_backtesting_trades)
self.trade_button.setEnabled(False)
self.daily_button = QtWidgets.QPushButton("每日盈亏")
self.daily_button.clicked.connect(self.show_daily_results)
self.daily_button.setEnabled(False)
for button in [
backtesting_button,
optimization_button,
downloading_button,
self.result_button
self.result_button,
self.order_button,
self.trade_button,
self.daily_button
]:
button.setFixedHeight(button.sizeHint().height() * 2)
@ -114,12 +130,16 @@ class BacktesterManager(QtWidgets.QWidget):
form.addRow("合约乘数", self.size_line)
form.addRow("价格跳动", self.pricetick_line)
form.addRow("回测资金", self.capital_line)
form.addRow(backtesting_button)
left_vbox = QtWidgets.QVBoxLayout()
left_vbox.addLayout(form)
left_vbox.addWidget(backtesting_button)
left_vbox.addWidget(downloading_button)
left_vbox.addStretch()
left_vbox.addWidget(self.trade_button)
left_vbox.addWidget(self.order_button)
left_vbox.addWidget(self.daily_button)
left_vbox.addStretch()
left_vbox.addWidget(optimization_button)
left_vbox.addWidget(self.result_button)
@ -132,6 +152,25 @@ class BacktesterManager(QtWidgets.QWidget):
self.chart = BacktesterChart()
self.chart.setMinimumWidth(1000)
self.trade_dialog = BacktestingResultDialog(
self.main_engine,
self.event_engine,
"回测成交记录",
BacktestingTradeMonitor
)
self.order_dialog = BacktestingResultDialog(
self.main_engine,
self.event_engine,
"回测委托记录",
BacktestingOrderMonitor
)
self.daily_dialog = BacktestingResultDialog(
self.main_engine,
self.event_engine,
"回测每日盈亏",
DailyResultMonitor
)
# Layout
vbox = QtWidgets.QVBoxLayout()
vbox.addWidget(self.statistics_monitor)
@ -176,6 +215,10 @@ class BacktesterManager(QtWidgets.QWidget):
df = self.backtester_engine.get_result_df()
self.chart.set_data(df)
self.trade_button.setEnabled(True)
self.order_button.setEnabled(True)
self.daily_button.setEnabled(True)
def process_optimization_finished_event(self, event: Event):
""""""
self.write_log("请点击[优化结果]按钮查看")
@ -220,6 +263,14 @@ class BacktesterManager(QtWidgets.QWidget):
if result:
self.statistics_monitor.clear_data()
self.chart.clear_data()
self.trade_button.setEnabled(False)
self.order_button.setEnabled(False)
self.daily_button.setEnabled(False)
self.trade_dialog.clear_data()
self.order_dialog.clear_data()
self.daily_dialog.clear_data()
def start_optimization(self):
""""""
@ -284,6 +335,30 @@ class BacktesterManager(QtWidgets.QWidget):
)
dialog.exec_()
def show_backtesting_trades(self):
""""""
if not self.trade_dialog.is_updated():
trades = self.backtester_engine.get_all_trades()
self.trade_dialog.update_data(trades)
self.trade_dialog.exec_()
def show_backtesting_orders(self):
""""""
if not self.order_dialog.is_updated():
orders = self.backtester_engine.get_all_orders()
self.order_dialog.update_data(orders)
self.order_dialog.exec_()
def show_daily_results(self):
""""""
if not self.daily_dialog.is_updated():
results = self.backtester_engine.get_all_daily_results()
self.daily_dialog.update_data(results)
self.daily_dialog.exec_()
def show(self):
""""""
self.showMaximized()
@ -750,3 +825,117 @@ class OptimizationResultMonitor(QtWidgets.QDialog):
vbox.addWidget(table)
self.setLayout(vbox)
class BacktestingTradeMonitor(BaseMonitor):
"""
Monitor for backtesting trade data.
"""
headers = {
"tradeid": {"display": "成交号 ", "cell": BaseCell, "update": False},
"orderid": {"display": "委托号", "cell": BaseCell, "update": False},
"symbol": {"display": "代码", "cell": BaseCell, "update": False},
"exchange": {"display": "交易所", "cell": EnumCell, "update": False},
"direction": {"display": "方向", "cell": DirectionCell, "update": False},
"offset": {"display": "开平", "cell": EnumCell, "update": False},
"price": {"display": "价格", "cell": BaseCell, "update": False},
"volume": {"display": "数量", "cell": BaseCell, "update": False},
"datetime": {"display": "时间", "cell": BaseCell, "update": False},
"gateway_name": {"display": "接口", "cell": BaseCell, "update": False},
}
class BacktestingOrderMonitor(BaseMonitor):
"""
Monitor for backtesting order data.
"""
headers = {
"orderid": {"display": "委托号", "cell": BaseCell, "update": False},
"symbol": {"display": "代码", "cell": BaseCell, "update": False},
"exchange": {"display": "交易所", "cell": EnumCell, "update": False},
"type": {"display": "类型", "cell": EnumCell, "update": False},
"direction": {"display": "方向", "cell": DirectionCell, "update": False},
"offset": {"display": "开平", "cell": EnumCell, "update": False},
"price": {"display": "价格", "cell": BaseCell, "update": False},
"volume": {"display": "总数量", "cell": BaseCell, "update": False},
"traded": {"display": "已成交", "cell": BaseCell, "update": False},
"status": {"display": "状态", "cell": EnumCell, "update": False},
"datetime": {"display": "时间", "cell": BaseCell, "update": False},
"gateway_name": {"display": "接口", "cell": BaseCell, "update": False},
}
class DailyResultMonitor(BaseMonitor):
"""
Monitor for backtesting daily result.
"""
headers = {
"date": {"display": "日期", "cell": BaseCell, "update": False},
"trade_count": {"display": "成交笔数", "cell": BaseCell, "update": False},
"start_pos": {"display": "开盘持仓", "cell": BaseCell, "update": False},
"end_pos": {"display": "收盘持仓", "cell": BaseCell, "update": False},
"turnover": {"display": "成交额", "cell": BaseCell, "update": False},
"commission": {"display": "手续费", "cell": BaseCell, "update": False},
"slippage": {"display": "滑点", "cell": BaseCell, "update": False},
"trading_pnl": {"display": "交易盈亏", "cell": BaseCell, "update": False},
"holding_pnl": {"display": "持仓盈亏", "cell": BaseCell, "update": False},
"total_pnl": {"display": "总盈亏", "cell": BaseCell, "update": False},
"net_pnl": {"display": "净盈亏", "cell": BaseCell, "update": False},
}
class BacktestingResultDialog(QtWidgets.QDialog):
"""
"""
def __init__(
self,
main_engine: MainEngine,
event_engine: EventEngine,
title: str,
table_class: QtWidgets.QTableWidget
):
""""""
super().__init__()
self.main_engine = main_engine
self.event_engine = event_engine
self.title = title
self.table_class = table_class
self.updated = False
self.init_ui()
def init_ui(self):
""""""
self.setWindowTitle(self.title)
self.resize(1100, 600)
self.table = self.table_class(self.main_engine, self.event_engine)
vbox = QtWidgets.QVBoxLayout()
vbox.addWidget(self.table)
self.setLayout(vbox)
def clear_data(self):
""""""
self.updated = False
self.table.setRowCount(0)
def update_data(self, data: list):
""""""
self.updated = True
data.reverse()
for obj in data:
self.table.insert_new_row(obj)
def is_updated(self):
""""""
return self.updated

View File

@ -1,5 +1,5 @@
from collections import defaultdict
from datetime import date, datetime
from datetime import date, datetime, timedelta
from typing import Callable
from itertools import product
from functools import lru_cache
@ -211,22 +211,44 @@ class BacktestingEngine:
""""""
self.output("开始加载历史数据")
if self.mode == BacktestingMode.BAR:
self.history_data = load_bar_data(
self.symbol,
self.exchange,
self.interval,
self.start,
self.end
)
else:
self.history_data = load_tick_data(
self.symbol,
self.exchange,
self.start,
self.end
)
if not self.end:
self.end = datetime.now()
# Load 30 days of data each time and allow for progress update
progress_delta = timedelta(days=30)
total_delta = self.end - self.start
start = self.start
end = self.start + progress_delta
progress = 0
while start < self.end:
if self.mode == BacktestingMode.BAR:
data = load_bar_data(
self.symbol,
self.exchange,
self.interval,
start,
end
)
else:
data = load_tick_data(
self.symbol,
self.exchange,
start,
end
)
self.history_data.extend(data)
progress += progress_delta / total_delta
progress = min(progress, 1)
progress_bar = "#" * int(progress * 10)
self.output(f"加载进度:{progress_bar} [{progress:.0%}]")
start = end
end += progress_delta
self.output(f"历史数据加载完成,数据量:{len(self.history_data)}")
def run_backtesting(self):
@ -803,6 +825,7 @@ class BacktestingEngine:
status=Status.ALLTRADED,
gateway_name=self.gateway_name,
)
order.datetime = self.datetime
self.limit_orders[order.vt_orderid] = order
@ -921,6 +944,7 @@ class BacktestingEngine:
status=Status.NOTTRADED,
gateway_name=self.gateway_name,
)
order.datetime = self.datetime
self.active_limit_orders[order.vt_orderid] = order
self.limit_orders[order.vt_orderid] = order
@ -997,6 +1021,23 @@ class BacktestingEngine:
"""
print(f"{datetime.now()}\t{msg}")
def get_all_trades(self):
"""
Return all trade data of current backtesting result.
"""
return list(self.trades.values())
def get_all_orders(self):
"""
Return all limit order data of current backtesting result.
"""
return list(self.limit_orders.values())
def get_all_daily_results(self):
"""
Return all daily result data.
"""
return list(self.daily_results.values())
class DailyResult:
""""""

View File

@ -64,7 +64,7 @@ class OnetokenGateway(BaseGateway):
default_setting = {
"OT Key": "",
"OT Secret": "",
"交易所": ["BINANCE", "BITMEX", "OKEX", "OKEF"],
"交易所": ["BINANCE", "BITMEX", "OKEX", "OKEF", "HUOBIP", "HUOBIF"],
"账户": "",
"会话数": 3,
"代理地址": "127.0.0.1",

View File

@ -19,7 +19,7 @@ from tigeropen.tiger_open_config import TigerOpenClientConfig
from tigeropen.common.consts import Language, Currency, Market
from tigeropen.quote.quote_client import QuoteClient
from tigeropen.trade.trade_client import TradeClient
from tigeropen.trade.domain.order import ORDER_STATUS
from tigeropen.trade.domain.order import OrderStatus
from tigeropen.push.push_client import PushClient
from tigeropen.common.exceptions import ApiException
@ -64,15 +64,15 @@ ORDERTYPE_VT2TIGER = {
}
STATUS_TIGER2VT = {
ORDER_STATUS.PENDING_NEW: Status.SUBMITTING,
ORDER_STATUS.NEW: Status.SUBMITTING,
ORDER_STATUS.HELD: Status.SUBMITTING,
ORDER_STATUS.PARTIALLY_FILLED: Status.PARTTRADED,
ORDER_STATUS.FILLED: Status.ALLTRADED,
ORDER_STATUS.CANCELLED: Status.CANCELLED,
ORDER_STATUS.PENDING_CANCEL: Status.CANCELLED,
ORDER_STATUS.REJECTED: Status.REJECTED,
ORDER_STATUS.EXPIRED: Status.NOTTRADED
OrderStatus.PENDING_NEW: Status.SUBMITTING,
OrderStatus.NEW: Status.SUBMITTING,
OrderStatus.HELD: Status.SUBMITTING,
OrderStatus.PARTIALLY_FILLED: Status.PARTTRADED,
OrderStatus.FILLED: Status.ALLTRADED,
OrderStatus.CANCELLED: Status.CANCELLED,
OrderStatus.PENDING_CANCEL: Status.CANCELLED,
OrderStatus.REJECTED: Status.REJECTED,
OrderStatus.EXPIRED: Status.NOTTRADED
}
PUSH_STATUS_TIGER2VT = {
@ -559,7 +559,7 @@ class TigerGateway(BaseGateway):
Process trade data for both query and update.
"""
for i in data:
if i.status == ORDER_STATUS.PARTIALLY_FILLED or i.status == ORDER_STATUS.FILLED:
if i.status == OrderStatus.PARTIALLY_FILLED or i.status == OrderStatus.FILLED:
symbol, exchange = convert_symbol_tiger2vt(str(i.contract))
self.tradeid += 1

View File

@ -237,8 +237,9 @@ class BaseMonitor(QtWidgets.QTableWidget):
"""
Register event handler into event engine.
"""
self.signal.connect(self.process_event)
self.event_engine.register(self.event_type, self.signal.emit)
if self.event_type:
self.signal.connect(self.process_event)
self.event_engine.register(self.event_type, self.signal.emit)
def process_event(self, event):
"""