From ddbf62d47afecfb16a65b689973bedd7e1094c9f Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sat, 9 Nov 2019 14:53:56 +0800 Subject: [PATCH 1/5] [Add] start developing spread backtesting function --- vnpy/app/spread_trading/backtesting.py | 716 +++++++++++++++++++++++++ vnpy/app/spread_trading/base.py | 5 + 2 files changed, 721 insertions(+) create mode 100644 vnpy/app/spread_trading/backtesting.py diff --git a/vnpy/app/spread_trading/backtesting.py b/vnpy/app/spread_trading/backtesting.py new file mode 100644 index 00000000..199c8065 --- /dev/null +++ b/vnpy/app/spread_trading/backtesting.py @@ -0,0 +1,716 @@ +from collections import defaultdict +from datetime import date, datetime, timedelta +from typing import Callable +from functools import lru_cache + +import numpy as np +import matplotlib.pyplot as plt +import seaborn as sns +from pandas import DataFrame + +from vnpy.trader.constant import (Direction, Offset, Exchange, + Interval, Status) +from vnpy.trader.database import database_manager +from vnpy.trader.object import TradeData, BarData, TickData +from vnpy.trader.utility import round_to + +from .template import SpreadStrategyTemplate +from .base import SpreadData, BacktestingMode + +sns.set_style("whitegrid") + + +class BacktestingEngine: + """""" + + gateway_name = "BACKTESTING" + + def __init__(self): + """""" + self.spread: SpreadData = None + + self.start = None + self.end = None + self.rate = 0 + self.slippage = 0 + self.size = 1 + self.pricetick = 0 + self.capital = 1_000_000 + self.mode = BacktestingMode.BAR + + self.strategy_class = None + self.strategy = None + self.tick: TickData = None + self.bar: BarData = None + self.datetime = None + + self.interval = None + self.days = 0 + self.callback = None + self.history_data = [] + + self.algo_count = 0 + self.algos = {} + self.active_algos = {} + + self.trade_count = 0 + self.trades = {} + + self.logs = [] + + self.daily_results = {} + self.daily_df = None + + def output(self, msg): + """ + Output message of backtesting engine. + """ + print(f"{datetime.now()}\t{msg}") + + def clear_data(self): + """ + Clear all data of last backtesting. + """ + self.strategy = None + self.tick = None + self.bar = None + self.datetime = None + + self.algo_count = 0 + self.algos.clear() + self.active_algos.clear() + + self.trade_count = 0 + self.trades.clear() + + self.logs.clear() + self.daily_results.clear() + + def set_parameters( + self, + spread: SpreadData, + interval: Interval, + start: datetime, + rate: float, + slippage: float, + size: float, + pricetick: float, + capital: int = 0, + end: datetime = None, + mode: BacktestingMode = BacktestingMode.BAR + ): + """""" + self.spread = spread + self.interval = Interval(interval) + self.rate = rate + self.slippage = slippage + self.size = size + self.pricetick = pricetick + self.start = start + self.capital = capital + self.end = end + self.mode = mode + + def add_strategy(self, strategy_class: type, setting: dict): + """""" + self.strategy_class = strategy_class + + self.strategy = strategy_class( + self, + strategy_class.__name__, + self.spread, + setting + ) + + def load_data(self): + """""" + self.output("开始加载历史数据") + + if not self.end: + self.end = datetime.now() + + if self.start >= self.end: + self.output("起始日期必须小于结束日期") + return + + if self.mode == BacktestingMode.BAR: + self.history_data = load_bar_data( + self.spread, + self.interval, + self.start, + self.end + ) + else: + self.history_datas = load_tick_data( + self.spread, + self.start, + self.end + ) + + self.output(f"历史数据加载完成,数据量:{len(self.history_data)}") + + def run_backtesting(self): + """""" + if self.mode == BacktestingMode.BAR: + func = self.new_bar + else: + func = self.new_tick + + self.strategy.on_init() + + # Use the first [days] of history data for initializing strategy + day_count = 0 + ix = 0 + + for ix, data in enumerate(self.history_data): + if self.datetime and data.datetime.day != self.datetime.day: + day_count += 1 + if day_count >= self.days: + break + + self.datetime = data.datetime + self.callback(data) + + self.strategy.inited = True + self.output("策略初始化完成") + + self.strategy.on_start() + self.strategy.trading = True + self.output("开始回放历史数据") + + # Use the rest of history data for running backtesting + for data in self.history_data[ix:]: + func(data) + + self.output("历史数据回放结束") + + def calculate_result(self): + """""" + self.output("开始计算逐日盯市盈亏") + + if not self.trades: + self.output("成交记录为空,无法计算") + return + + # Add trade data into daily reuslt. + for trade in self.trades.values(): + d = trade.datetime.date() + daily_result = self.daily_results[d] + daily_result.add_trade(trade) + + # Calculate daily result by iteration. + pre_close = 0 + start_pos = 0 + + for daily_result in self.daily_results.values(): + daily_result.calculate_pnl( + pre_close, + start_pos, + self.size, + self.rate, + self.slippage, + self.inverse + ) + + pre_close = daily_result.close_price + start_pos = daily_result.end_pos + + # Generate dataframe + results = defaultdict(list) + + for daily_result in self.daily_results.values(): + for key, value in daily_result.__dict__.items(): + results[key].append(value) + + self.daily_df = DataFrame.from_dict(results).set_index("date") + + self.output("逐日盯市盈亏计算完成") + return self.daily_df + + def calculate_statistics(self, df: DataFrame = None, output=True): + """""" + self.output("开始计算策略统计指标") + + # Check DataFrame input exterior + if df is None: + df = self.daily_df + + # Check for init DataFrame + if df is None: + # Set all statistics to 0 if no trade. + start_date = "" + end_date = "" + total_days = 0 + profit_days = 0 + loss_days = 0 + end_balance = 0 + max_drawdown = 0 + max_ddpercent = 0 + max_drawdown_duration = 0 + total_net_pnl = 0 + daily_net_pnl = 0 + total_commission = 0 + daily_commission = 0 + total_slippage = 0 + daily_slippage = 0 + total_turnover = 0 + daily_turnover = 0 + total_trade_count = 0 + daily_trade_count = 0 + total_return = 0 + annual_return = 0 + daily_return = 0 + return_std = 0 + sharpe_ratio = 0 + return_drawdown_ratio = 0 + else: + # Calculate balance related time series data + df["balance"] = df["net_pnl"].cumsum() + self.capital + df["return"] = np.log(df["balance"] / df["balance"].shift(1)).fillna(0) + df["highlevel"] = ( + df["balance"].rolling( + min_periods=1, window=len(df), center=False).max() + ) + df["drawdown"] = df["balance"] - df["highlevel"] + df["ddpercent"] = df["drawdown"] / df["highlevel"] * 100 + + # Calculate statistics value + start_date = df.index[0] + end_date = df.index[-1] + + total_days = len(df) + profit_days = len(df[df["net_pnl"] > 0]) + loss_days = len(df[df["net_pnl"] < 0]) + + end_balance = df["balance"].iloc[-1] + max_drawdown = df["drawdown"].min() + max_ddpercent = df["ddpercent"].min() + max_drawdown_end = df["drawdown"].idxmin() + max_drawdown_start = df["balance"][:max_drawdown_end].argmax() + max_drawdown_duration = (max_drawdown_end - max_drawdown_start).days + + total_net_pnl = df["net_pnl"].sum() + daily_net_pnl = total_net_pnl / total_days + + total_commission = df["commission"].sum() + daily_commission = total_commission / total_days + + total_slippage = df["slippage"].sum() + daily_slippage = total_slippage / total_days + + total_turnover = df["turnover"].sum() + daily_turnover = total_turnover / total_days + + total_trade_count = df["trade_count"].sum() + daily_trade_count = total_trade_count / total_days + + total_return = (end_balance / self.capital - 1) * 100 + annual_return = total_return / total_days * 240 + daily_return = df["return"].mean() * 100 + return_std = df["return"].std() * 100 + + if return_std: + sharpe_ratio = daily_return / return_std * np.sqrt(240) + else: + sharpe_ratio = 0 + + return_drawdown_ratio = -total_return / max_ddpercent + + # Output + if output: + self.output("-" * 30) + self.output(f"首个交易日:\t{start_date}") + self.output(f"最后交易日:\t{end_date}") + + self.output(f"总交易日:\t{total_days}") + self.output(f"盈利交易日:\t{profit_days}") + self.output(f"亏损交易日:\t{loss_days}") + + self.output(f"起始资金:\t{self.capital:,.2f}") + self.output(f"结束资金:\t{end_balance:,.2f}") + + self.output(f"总收益率:\t{total_return:,.2f}%") + self.output(f"年化收益:\t{annual_return:,.2f}%") + self.output(f"最大回撤: \t{max_drawdown:,.2f}") + self.output(f"百分比最大回撤: {max_ddpercent:,.2f}%") + self.output(f"最长回撤天数: \t{max_drawdown_duration}") + + self.output(f"总盈亏:\t{total_net_pnl:,.2f}") + self.output(f"总手续费:\t{total_commission:,.2f}") + self.output(f"总滑点:\t{total_slippage:,.2f}") + self.output(f"总成交金额:\t{total_turnover:,.2f}") + self.output(f"总成交笔数:\t{total_trade_count}") + + self.output(f"日均盈亏:\t{daily_net_pnl:,.2f}") + self.output(f"日均手续费:\t{daily_commission:,.2f}") + self.output(f"日均滑点:\t{daily_slippage:,.2f}") + self.output(f"日均成交金额:\t{daily_turnover:,.2f}") + self.output(f"日均成交笔数:\t{daily_trade_count}") + + self.output(f"日均收益率:\t{daily_return:,.2f}%") + self.output(f"收益标准差:\t{return_std:,.2f}%") + self.output(f"Sharpe Ratio:\t{sharpe_ratio:,.2f}") + self.output(f"收益回撤比:\t{return_drawdown_ratio:,.2f}") + + statistics = { + "start_date": start_date, + "end_date": end_date, + "total_days": total_days, + "profit_days": profit_days, + "loss_days": loss_days, + "capital": self.capital, + "end_balance": end_balance, + "max_drawdown": max_drawdown, + "max_ddpercent": max_ddpercent, + "max_drawdown_duration": max_drawdown_duration, + "total_net_pnl": total_net_pnl, + "daily_net_pnl": daily_net_pnl, + "total_commission": total_commission, + "daily_commission": daily_commission, + "total_slippage": total_slippage, + "daily_slippage": daily_slippage, + "total_turnover": total_turnover, + "daily_turnover": daily_turnover, + "total_trade_count": total_trade_count, + "daily_trade_count": daily_trade_count, + "total_return": total_return, + "annual_return": annual_return, + "daily_return": daily_return, + "return_std": return_std, + "sharpe_ratio": sharpe_ratio, + "return_drawdown_ratio": return_drawdown_ratio, + } + + return statistics + + def show_chart(self, df: DataFrame = None): + """""" + # Check DataFrame input exterior + if df is None: + df = self.daily_df + + # Check for init DataFrame + if df is None: + return + + plt.figure(figsize=(10, 16)) + + balance_plot = plt.subplot(4, 1, 1) + balance_plot.set_title("Balance") + df["balance"].plot(legend=True) + + drawdown_plot = plt.subplot(4, 1, 2) + drawdown_plot.set_title("Drawdown") + drawdown_plot.fill_between(range(len(df)), df["drawdown"].values) + + pnl_plot = plt.subplot(4, 1, 3) + pnl_plot.set_title("Daily Pnl") + df["net_pnl"].plot(kind="bar", legend=False, grid=False, xticks=[]) + + distribution_plot = plt.subplot(4, 1, 4) + distribution_plot.set_title("Daily Pnl Distribution") + df["net_pnl"].hist(bins=50) + + plt.show() + + def update_daily_close(self, price: float): + """""" + d = self.datetime.date() + + daily_result = self.daily_results.get(d, None) + if daily_result: + daily_result.close_price = price + else: + self.daily_results[d] = DailyResult(d, price) + + def new_bar(self, bar: BarData): + """""" + self.bar = bar + self.datetime = bar.datetime + + self.cross_limit_order() + self.cross_stop_order() + self.strategy.on_bar(bar) + + self.update_daily_close(bar.close_price) + + def new_tick(self, tick: TickData): + """""" + self.tick = tick + self.datetime = tick.datetime + + self.cross_limit_order() + self.cross_stop_order() + self.strategy.on_tick(tick) + + self.update_daily_close(tick.last_price) + + def cross_limit_order(self): + """ + Cross limit order with last bar/tick data. + """ + if self.mode == BacktestingMode.BAR: + long_cross_price = self.bar.low_price + short_cross_price = self.bar.high_price + long_best_price = self.bar.open_price + short_best_price = self.bar.open_price + else: + long_cross_price = self.tick.ask_price_1 + short_cross_price = self.tick.bid_price_1 + long_best_price = long_cross_price + short_best_price = short_cross_price + + for order in list(self.active_limit_orders.values()): + # Push order update with status "not traded" (pending). + if order.status == Status.SUBMITTING: + order.status = Status.NOTTRADED + self.strategy.on_order(order) + + # Check whether limit orders can be filled. + long_cross = ( + order.direction == Direction.LONG + and order.price >= long_cross_price + and long_cross_price > 0 + ) + + short_cross = ( + order.direction == Direction.SHORT + and order.price <= short_cross_price + and short_cross_price > 0 + ) + + if not long_cross and not short_cross: + continue + + # Push order udpate with status "all traded" (filled). + order.traded = order.volume + order.status = Status.ALLTRADED + self.strategy.on_order(order) + + self.active_limit_orders.pop(order.vt_orderid) + + # Push trade update + self.trade_count += 1 + + if long_cross: + trade_price = min(order.price, long_best_price) + pos_change = order.volume + else: + trade_price = max(order.price, short_best_price) + pos_change = -order.volume + + trade = TradeData( + symbol=order.symbol, + exchange=order.exchange, + orderid=order.orderid, + tradeid=str(self.trade_count), + direction=order.direction, + offset=order.offset, + price=trade_price, + volume=order.volume, + time=self.datetime.strftime("%H:%M:%S"), + gateway_name=self.gateway_name, + ) + trade.datetime = self.datetime + + self.strategy.pos += pos_change + self.strategy.on_trade(trade) + + self.trades[trade.vt_tradeid] = trade + + def load_bar( + self, spread: str, days: int, interval: Interval, callback: Callable + ): + """""" + self.days = days + self.callback = callback + + def load_tick(self, spread: str, days: int, callback: Callable): + """""" + self.days = days + self.callback = callback + + def start_algo( + self, + strategy: SpreadStrategyTemplate, + spread_name: str, + direction: Direction, + offset: Offset, + price: float, + volume: float, + payup: int, + interval: int, + lock: bool + ) -> str: + """""" + pass + + def stop_algo( + self, + algoid: str + ): + """""" + pass + + def send_order( + self, + strategy: SpreadStrategyTemplate, + direction: Direction, + offset: Offset, + price: float, + volume: float, + stop: bool, + lock: bool + ): + """""" + price = round_to(price, self.pricetick) + if stop: + vt_orderid = self.send_stop_order(direction, offset, price, volume) + else: + vt_orderid = self.send_limit_order(direction, offset, price, volume) + return [vt_orderid] + + def cancel_order(self, strategy: SpreadStrategyTemplate, vt_orderid: str): + """ + Cancel order by vt_orderid. + """ + if vt_orderid.startswith(STOPORDER_PREFIX): + self.cancel_stop_order(strategy, vt_orderid) + else: + self.cancel_limit_order(strategy, vt_orderid) + + def write_log(self, msg: str, strategy: SpreadStrategyTemplate = None): + """ + Write log message. + """ + msg = f"{self.datetime}\t{msg}" + self.logs.append(msg) + + def send_email(self, msg: str, strategy: SpreadStrategyTemplate = None): + """ + Send email to default receiver. + """ + pass + + def put_strategy_event(self, strategy: SpreadStrategyTemplate): + """ + Put an event to update strategy status. + """ + pass + + +class DailyResult: + """""" + + def __init__(self, date: date, close_price: float): + """""" + self.date = date + self.close_price = close_price + self.pre_close = 0 + + self.trades = [] + self.trade_count = 0 + + self.start_pos = 0 + self.end_pos = 0 + + self.turnover = 0 + self.commission = 0 + self.slippage = 0 + + self.trading_pnl = 0 + self.holding_pnl = 0 + self.total_pnl = 0 + self.net_pnl = 0 + + def add_trade(self, trade: TradeData): + """""" + self.trades.append(trade) + + def calculate_pnl( + self, + pre_close: float, + start_pos: float, + size: int, + rate: float, + slippage: float, + inverse: bool + ): + """""" + # If no pre_close provided on the first day, + # use value 1 to avoid zero division error + if pre_close: + self.pre_close = pre_close + else: + self.pre_close = 1 + + # Holding pnl is the pnl from holding position at day start + self.start_pos = start_pos + self.end_pos = start_pos + + if not inverse: # For normal contract + self.holding_pnl = self.start_pos * \ + (self.close_price - self.pre_close) * size + else: # For crypto currency inverse contract + self.holding_pnl = self.start_pos * \ + (1 / self.pre_close - 1 / self.close_price) * size + + # Trading pnl is the pnl from new trade during the day + self.trade_count = len(self.trades) + + for trade in self.trades: + if trade.direction == Direction.LONG: + pos_change = trade.volume + else: + pos_change = -trade.volume + + self.end_pos += pos_change + + # For normal contract + if not inverse: + turnover = trade.volume * size * trade.price + self.trading_pnl += pos_change * \ + (self.close_price - trade.price) * size + self.slippage += trade.volume * size * slippage + # For crypto currency inverse contract + else: + turnover = trade.volume * size / trade.price + self.trading_pnl += pos_change * \ + (1 / trade.price - 1 / self.close_price) * size + self.slippage += trade.volume * size * slippage / (trade.price ** 2) + + self.turnover += turnover + self.commission += turnover * rate + + # Net pnl takes account of commission and slippage cost + self.total_pnl = self.trading_pnl + self.holding_pnl + self.net_pnl = self.total_pnl - self.commission - self.slippage + + + +@lru_cache(maxsize=999) +def load_bar_data( + symbol: str, + exchange: Exchange, + interval: Interval, + start: datetime, + end: datetime +): + """""" + return database_manager.load_bar_data( + symbol, exchange, interval, start, end + ) + + +@lru_cache(maxsize=999) +def load_tick_data( + symbol: str, + exchange: Exchange, + start: datetime, + end: datetime +): + """""" + return database_manager.load_tick_data( + symbol, exchange, start, end + ) + diff --git a/vnpy/app/spread_trading/base.py b/vnpy/app/spread_trading/base.py index efbe185f..374e48b5 100644 --- a/vnpy/app/spread_trading/base.py +++ b/vnpy/app/spread_trading/base.py @@ -347,3 +347,8 @@ def calculate_inverse_volume( if not price: return 0 return original_volume * size / price + + +class BacktestingMode(Enum): + BAR = 1 + TICK = 2 From 687bdbc66db352cf16feee619a6b39f0bd100546 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sun, 10 Nov 2019 16:09:43 +0800 Subject: [PATCH 2/5] [Add] backtesting function for spread trading --- examples/spread_backtesting/backtesting.ipynb | 120 ++++++++++ vnpy/app/spread_trading/__init__.py | 4 +- vnpy/app/spread_trading/backtesting.py | 225 +++++++++++------- vnpy/app/spread_trading/base.py | 1 + vnpy/app/spread_trading/template.py | 42 +++- 5 files changed, 297 insertions(+), 95 deletions(-) create mode 100644 examples/spread_backtesting/backtesting.ipynb diff --git a/examples/spread_backtesting/backtesting.ipynb b/examples/spread_backtesting/backtesting.ipynb new file mode 100644 index 00000000..66750ab0 --- /dev/null +++ b/examples/spread_backtesting/backtesting.ipynb @@ -0,0 +1,120 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "#%%\n", + "from vnpy.app.spread_trading.backtesting import BacktestingEngine\n", + "from vnpy.app.spread_trading.strategies.statistical_arbitrage_strategy import (\n", + " StatisticalArbitrageStrategy\n", + ")\n", + "from vnpy.app.spread_trading.base import LegData, SpreadData\n", + "from datetime import datetime" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "spread = SpreadData(\n", + " name=\"IF-Spread\",\n", + " legs=[LegData(\"IF1911.CFFEX\"), LegData(\"IF1912.CFFEX\")],\n", + " price_multipliers={\"IF1911.CFFEX\": 1, \"IF1912.CFFEX\": -1},\n", + " trading_multipliers={\"IF1911.CFFEX\": 1, \"IF1912.CFFEX\": -1},\n", + " active_symbol=\"IF1911.CFFEX\",\n", + " inverse_contracts={\"IF1911.CFFEX\": False, \"IF1912.CFFEX\": False},\n", + " min_volume=1\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "#%%\n", + "engine = BacktestingEngine()\n", + "engine.set_parameters(\n", + " spread=spread,\n", + " interval=\"1m\",\n", + " start=datetime(2019, 6, 10),\n", + " end=datetime(2019, 11, 10),\n", + " rate=0,\n", + " slippage=0,\n", + " size=300,\n", + " pricetick=0.2,\n", + " capital=1_000_000,\n", + ")\n", + "engine.add_strategy(StatisticalArbitrageStrategy, {})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2019-11-10 16:09:03.822440\t开始加载历史数据\n" + ] + } + ], + "source": [ + "#%%\n", + "engine.load_data()\n", + "engine.run_backtesting()\n", + "df = engine.calculate_result()\n", + "engine.calculate_statistics()\n", + "engine.show_chart()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for trade in engine.trades.values():\n", + " print(trade)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.1" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/vnpy/app/spread_trading/__init__.py b/vnpy/app/spread_trading/__init__.py index 360a4390..f73fe014 100644 --- a/vnpy/app/spread_trading/__init__.py +++ b/vnpy/app/spread_trading/__init__.py @@ -3,7 +3,9 @@ from pathlib import Path from vnpy.trader.app import BaseApp from vnpy.trader.object import ( OrderData, - TradeData + TradeData, + TickData, + BarData ) from .engine import ( diff --git a/vnpy/app/spread_trading/backtesting.py b/vnpy/app/spread_trading/backtesting.py index 199c8065..20dd8419 100644 --- a/vnpy/app/spread_trading/backtesting.py +++ b/vnpy/app/spread_trading/backtesting.py @@ -1,6 +1,6 @@ from collections import defaultdict from datetime import date, datetime, timedelta -from typing import Callable +from typing import Callable, Type, Dict, List from functools import lru_cache import numpy as np @@ -14,7 +14,7 @@ from vnpy.trader.database import database_manager from vnpy.trader.object import TradeData, BarData, TickData from vnpy.trader.utility import round_to -from .template import SpreadStrategyTemplate +from .template import SpreadStrategyTemplate, SpreadAlgoTemplate from .base import SpreadData, BacktestingMode sns.set_style("whitegrid") @@ -38,8 +38,8 @@ class BacktestingEngine: self.capital = 1_000_000 self.mode = BacktestingMode.BAR - self.strategy_class = None - self.strategy = None + self.strategy_class: Type[SpreadStrategyTemplate] = None + self.strategy: SpreadStrategyTemplate = None self.tick: TickData = None self.bar: BarData = None self.datetime = None @@ -138,7 +138,8 @@ class BacktestingEngine: self.spread, self.interval, self.start, - self.end + self.end, + self.pricetick ) else: self.history_datas = load_tick_data( @@ -208,8 +209,7 @@ class BacktestingEngine: start_pos, self.size, self.rate, - self.slippage, - self.inverse + self.slippage ) pre_close = daily_result.close_price @@ -427,10 +427,9 @@ class BacktestingEngine: """""" self.bar = bar self.datetime = bar.datetime + self.cross_algo() - self.cross_limit_order() - self.cross_stop_order() - self.strategy.on_bar(bar) + self.strategy.on_spread_bar(bar) self.update_daily_close(bar.close_price) @@ -438,44 +437,39 @@ class BacktestingEngine: """""" self.tick = tick self.datetime = tick.datetime + self.cross_algo() - self.cross_limit_order() - self.cross_stop_order() - self.strategy.on_tick(tick) + self.spread.bid_price = tick.bid_price_1 + self.spread.bid_volume = tick.bid_volume_1 + self.spread.ask_price = tick.ask_price_1 + self.spread.ask_volume = tick.ask_volume_1 + + self.strategy.on_spread_data() self.update_daily_close(tick.last_price) - def cross_limit_order(self): + def cross_algo(self): """ Cross limit order with last bar/tick data. """ if self.mode == BacktestingMode.BAR: - long_cross_price = self.bar.low_price - short_cross_price = self.bar.high_price - long_best_price = self.bar.open_price - short_best_price = self.bar.open_price + long_cross_price = self.bar.close_price + short_cross_price = self.bar.close_price else: long_cross_price = self.tick.ask_price_1 short_cross_price = self.tick.bid_price_1 - long_best_price = long_cross_price - short_best_price = short_cross_price - - for order in list(self.active_limit_orders.values()): - # Push order update with status "not traded" (pending). - if order.status == Status.SUBMITTING: - order.status = Status.NOTTRADED - self.strategy.on_order(order) + for algo in list(self.active_algos.values()): # Check whether limit orders can be filled. long_cross = ( - order.direction == Direction.LONG - and order.price >= long_cross_price + algo.direction == Direction.LONG + and algo.price >= long_cross_price and long_cross_price > 0 ) short_cross = ( - order.direction == Direction.SHORT - and order.price <= short_cross_price + algo.direction == Direction.SHORT + and algo.price <= short_cross_price and short_cross_price > 0 ) @@ -483,49 +477,49 @@ class BacktestingEngine: continue # Push order udpate with status "all traded" (filled). - order.traded = order.volume - order.status = Status.ALLTRADED - self.strategy.on_order(order) + algo.traded = algo.volume + algo.status = Status.ALLTRADED + self.strategy.update_spread_algo(algo) - self.active_limit_orders.pop(order.vt_orderid) + self.active_algos.pop(algo.algoid) # Push trade update self.trade_count += 1 if long_cross: - trade_price = min(order.price, long_best_price) - pos_change = order.volume + trade_price = long_cross_price + pos_change = algo.volume else: - trade_price = max(order.price, short_best_price) - pos_change = -order.volume + trade_price = short_cross_price + pos_change = -algo.volume trade = TradeData( - symbol=order.symbol, - exchange=order.exchange, - orderid=order.orderid, + symbol=self.spread.name, + exchange=Exchange.LOCAL, + orderid=algo.algoid, tradeid=str(self.trade_count), - direction=order.direction, - offset=order.offset, + direction=algo.direction, + offset=algo.offset, price=trade_price, - volume=order.volume, + volume=algo.volume, time=self.datetime.strftime("%H:%M:%S"), gateway_name=self.gateway_name, ) trade.datetime = self.datetime - self.strategy.pos += pos_change - self.strategy.on_trade(trade) + self.spread.net_pos += pos_change + self.strategy.on_spread_pos() self.trades[trade.vt_tradeid] = trade def load_bar( - self, spread: str, days: int, interval: Interval, callback: Callable + self, spread: SpreadData, days: int, interval: Interval, callback: Callable ): """""" self.days = days self.callback = callback - def load_tick(self, spread: str, days: int, callback: Callable): + def load_tick(self, spread: SpreadData, days: int, callback: Callable): """""" self.days = days self.callback = callback @@ -543,14 +537,39 @@ class BacktestingEngine: lock: bool ) -> str: """""" - pass + self.algo_count += 1 + algoid = str(self.algo_count) + + algo = SpreadAlgoTemplate( + self, + algoid, + self.spread, + direction, + offset, + price, + volume, + payup, + interval, + lock + ) + + self.algos[algoid] = algo + self.active_algos[algoid] = algo + + return algoid def stop_algo( self, + strategy: SpreadStrategyTemplate, algoid: str ): """""" - pass + if algoid not in self.active_algos: + return + algo = self.active_algos.pop(algoid) + + algo.status = Status.CANCELLED + self.strategy.update_spread_algo(algo) def send_order( self, @@ -563,23 +582,15 @@ class BacktestingEngine: lock: bool ): """""" - price = round_to(price, self.pricetick) - if stop: - vt_orderid = self.send_stop_order(direction, offset, price, volume) - else: - vt_orderid = self.send_limit_order(direction, offset, price, volume) - return [vt_orderid] + pass def cancel_order(self, strategy: SpreadStrategyTemplate, vt_orderid: str): """ Cancel order by vt_orderid. """ - if vt_orderid.startswith(STOPORDER_PREFIX): - self.cancel_stop_order(strategy, vt_orderid) - else: - self.cancel_limit_order(strategy, vt_orderid) + pass - def write_log(self, msg: str, strategy: SpreadStrategyTemplate = None): + def write_strategy_log(self, strategy: SpreadStrategyTemplate, msg: str): """ Write log message. """ @@ -598,6 +609,10 @@ class BacktestingEngine: """ pass + def write_algo_log(self, algo: SpreadAlgoTemplate, msg: str): + """""" + pass + class DailyResult: """""" @@ -633,8 +648,7 @@ class DailyResult: start_pos: float, size: int, rate: float, - slippage: float, - inverse: bool + slippage: float ): """""" # If no pre_close provided on the first day, @@ -648,12 +662,7 @@ class DailyResult: self.start_pos = start_pos self.end_pos = start_pos - if not inverse: # For normal contract - self.holding_pnl = self.start_pos * \ - (self.close_price - self.pre_close) * size - else: # For crypto currency inverse contract - self.holding_pnl = self.start_pos * \ - (1 / self.pre_close - 1 / self.close_price) * size + self.holding_pnl = self.start_pos * (self.close_price - self.pre_close) * size # Trading pnl is the pnl from new trade during the day self.trade_count = len(self.trades) @@ -666,18 +675,10 @@ class DailyResult: self.end_pos += pos_change - # For normal contract - if not inverse: - turnover = trade.volume * size * trade.price - self.trading_pnl += pos_change * \ - (self.close_price - trade.price) * size - self.slippage += trade.volume * size * slippage - # For crypto currency inverse contract - else: - turnover = trade.volume * size / trade.price - self.trading_pnl += pos_change * \ - (1 / trade.price - 1 / self.close_price) * size - self.slippage += trade.volume * size * slippage / (trade.price ** 2) + turnover = trade.volume * size * trade.price + self.trading_pnl += pos_change * \ + (self.close_price - trade.price) * size + self.slippage += trade.volume * size * slippage self.turnover += turnover self.commission += turnover * rate @@ -687,30 +688,72 @@ class DailyResult: self.net_pnl = self.total_pnl - self.commission - self.slippage - @lru_cache(maxsize=999) def load_bar_data( - symbol: str, - exchange: Exchange, + spread: SpreadData, interval: Interval, start: datetime, - end: datetime + end: datetime, + pricetick: float ): """""" - return database_manager.load_bar_data( - symbol, exchange, interval, start, end - ) + # Load bar data of each spread leg + leg_bars: Dict[str, Dict] = {} + + for vt_symbol in spread.legs.keys(): + symbol, exchange_str = vt_symbol.split(".") + exchange = Exchange(exchange_str) + + bar_data: List[BarData] = database_manager.load_bar_data( + symbol, exchange, interval, start, end + ) + + bars: Dict[datetime, BarData] = {bar.datetime: bar for bar in bar_data} + leg_bars[vt_symbol] = bars + + # Calculate spread bar data + spread_bars: List[BarData] = [] + + for dt in bars.keys(): + spread_price = 0 + spread_available = True + + for leg in spread.legs.values(): + leg_bar = leg_bars[leg.vt_symbol].get(dt, None) + + if leg_bar: + price_multiplier = spread.price_multipliers[leg.vt_symbol] + spread_price += price_multiplier * leg_bar.close_price + else: + spread_available = False + + if spread_available: + spread_price = round_to(spread_price, pricetick) + + spread_bar = BarData( + symbol=spread.name, + exchange=exchange.LOCAL, + datetime=dt, + interval=interval, + open_price=spread_price, + high_price=spread_price, + low_price=spread_price, + close_price=spread_price, + gateway_name=BacktestingEngine.gateway_name, + ) + spread_bars.append(spread_bar) + + return spread_bars @lru_cache(maxsize=999) def load_tick_data( - symbol: str, - exchange: Exchange, + spread: SpreadData, start: datetime, end: datetime ): """""" return database_manager.load_tick_data( - symbol, exchange, start, end + spread.name, Exchange.LOCAL, start, end ) diff --git a/vnpy/app/spread_trading/base.py b/vnpy/app/spread_trading/base.py index 374e48b5..cace98f3 100644 --- a/vnpy/app/spread_trading/base.py +++ b/vnpy/app/spread_trading/base.py @@ -1,5 +1,6 @@ from typing import Dict, List from datetime import datetime +from enum import Enum from vnpy.trader.object import TickData, PositionData, TradeData, ContractData from vnpy.trader.constant import Direction, Offset, Exchange diff --git a/vnpy/app/spread_trading/template.py b/vnpy/app/spread_trading/template.py index 0d8838f3..de06bc25 100644 --- a/vnpy/app/spread_trading/template.py +++ b/vnpy/app/spread_trading/template.py @@ -1,10 +1,12 @@ from collections import defaultdict -from typing import Dict, List, Set +from typing import Dict, List, Set, Callable from copy import copy -from vnpy.trader.object import TickData, TradeData, OrderData, ContractData -from vnpy.trader.constant import Direction, Status, Offset +from vnpy.trader.object import ( + TickData, TradeData, OrderData, ContractData, BarData +) +from vnpy.trader.constant import Direction, Status, Offset, Interval from vnpy.trader.utility import virtual, floor_to, ceil_to, round_to from .base import SpreadData, calculate_inverse_volume @@ -434,6 +436,20 @@ class SpreadStrategyTemplate: """ pass + @virtual + def on_spread_tick(self, tick: TickData): + """ + Callback when new spread tick data is generated. + """ + pass + + @virtual + def on_spread_bar(self, bar: BarData): + """ + Callback when new spread bar data is generated. + """ + pass + @virtual def on_spread_pos(self): """ @@ -635,3 +651,23 @@ class SpreadStrategyTemplate: """ if self.inited: self.strategy_engine.send_email(msg, self) + + def load_bar( + self, + days: int, + interval: Interval = Interval.MINUTE, + callback: Callable = None, + ): + """ + Load historical bar data for initializing strategy. + """ + if not callback: + callback = self.on_spread_bar + + self.strategy_engine.load_bar(self.spread, days, interval, callback) + + def load_tick(self, days: int): + """ + Load historical tick data for initializing strategy. + """ + self.strategy_engine.load_tick(self.spread, days, self.on_spread_tick) From 9a5cdbe3e864b6e9da952cd915bd39983f745d85 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sun, 10 Nov 2019 16:11:05 +0800 Subject: [PATCH 3/5] [Add] statitical arbitrage strategy, close #1382 --- .../statistical_arbitrage_strategy.py | 176 ++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 vnpy/app/spread_trading/strategies/statistical_arbitrage_strategy.py diff --git a/vnpy/app/spread_trading/strategies/statistical_arbitrage_strategy.py b/vnpy/app/spread_trading/strategies/statistical_arbitrage_strategy.py new file mode 100644 index 00000000..173c12ec --- /dev/null +++ b/vnpy/app/spread_trading/strategies/statistical_arbitrage_strategy.py @@ -0,0 +1,176 @@ +from vnpy.trader.utility import BarGenerator, ArrayManager +from vnpy.app.spread_trading import ( + SpreadStrategyTemplate, + SpreadAlgoTemplate, + SpreadData, + OrderData, + TradeData, + TickData, + BarData +) + + +class StatisticalArbitrageStrategy(SpreadStrategyTemplate): + """""" + + author = "用Python的交易员" + + boll_window = 20 + boll_dev = 2 + max_pos = 10 + payup = 10 + interval = 5 + + spread_pos = 0.0 + boll_up = 0.0 + boll_down = 0.0 + boll_mid = 0.0 + + parameters = [ + "boll_window", + "boll_dev", + "max_pos", + "payup", + "interval" + ] + variables = [ + "spread_pos", + "boll_up", + "boll_down", + "boll_mid" + ] + + def __init__( + self, + strategy_engine, + strategy_name: str, + spread: SpreadData, + setting: dict + ): + """""" + super().__init__( + strategy_engine, strategy_name, spread, setting + ) + + self.bg = BarGenerator(self.on_spread_bar) + self.am = ArrayManager() + + def on_init(self): + """ + Callback when strategy is inited. + """ + self.write_log("策略初始化") + + self.load_bar(10) + + def on_start(self): + """ + Callback when strategy is started. + """ + self.write_log("策略启动") + + def on_stop(self): + """ + Callback when strategy is stopped. + """ + self.write_log("策略停止") + + self.put_event() + + def on_spread_data(self): + """ + Callback when spread price is updated. + """ + tick = self.get_spread_tick() + self.on_spread_tick(tick) + + def on_spread_tick(self, tick: TickData): + """ + Callback when new spread tick data is generated. + """ + self.bg.update_tick(tick) + + def on_spread_bar(self, bar: BarData): + """ + Callback when spread bar data is generated. + """ + self.am.update_bar(bar) + if not self.am.inited: + return + + self.boll_mid = self.am.sma(self.boll_window) + self.boll_up, self.boll_down = self.am.boll( + self.boll_window, self.boll_dev) + + if not self.spread_pos: + if bar.close_price >= self.boll_up: + self.start_short_algo( + bar.close_price - 10, + self.max_pos, + payup=self.payup, + interval=self.interval + ) + elif bar.close_price <= self.boll_down: + self.start_long_algo( + bar.close_price + 10, + self.max_pos, + payup=self.payup, + interval=self.interval + ) + elif self.spread_pos < 0: + if bar.close_price <= self.boll_mid: + self.start_long_algo( + bar.close_price + 10, + abs(self.spread_pos), + payup=self.payup, + interval=self.interval + ) + else: + if bar.close_price >= self.boll_mid: + self.start_short_algo( + bar.close_price - 10, + abs(self.spread_pos), + payup=self.payup, + interval=self.interval + ) + + def on_spread_pos(self): + """ + Callback when spread position is updated. + """ + self.spread_pos = self.get_spread_pos() + self.put_event() + + def on_spread_algo(self, algo: SpreadAlgoTemplate): + """ + Callback when algo status is updated. + """ + pass + + def on_order(self, order: OrderData): + """ + Callback when order status is updated. + """ + pass + + def on_trade(self, trade: TradeData): + """ + Callback when new trade data is received. + """ + pass + + def stop_open_algos(self): + """""" + if self.buy_algoid: + self.stop_algo(self.buy_algoid) + + if self.short_algoid: + self.stop_algo(self.short_algoid) + + def stop_close_algos(self): + """""" + if self.sell_algoid: + self.stop_algo(self.sell_algoid) + + if self.cover_algoid: + self.stop_algo(self.cover_algoid) From 1cb8233755b4ca2b9822c073ebd1dab865b1b94d Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sun, 10 Nov 2019 16:20:20 +0800 Subject: [PATCH 4/5] [Mod] flake8 code improve --- examples/spread_backtesting/backtesting.ipynb | 64 ++++++++++++++++++- vnpy/app/spread_trading/backtesting.py | 3 +- 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/examples/spread_backtesting/backtesting.ipynb b/examples/spread_backtesting/backtesting.ipynb index 66750ab0..c914ecc6 100644 --- a/examples/spread_backtesting/backtesting.ipynb +++ b/examples/spread_backtesting/backtesting.ipynb @@ -56,7 +56,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": { "scrolled": false }, @@ -65,8 +65,68 @@ "name": "stdout", "output_type": "stream", "text": [ - "2019-11-10 16:09:03.822440\t开始加载历史数据\n" + "2019-11-10 16:09:03.822440\t开始加载历史数据\n", + "2019-11-10 16:09:07.915724\t历史数据加载完成,数据量:7200\n", + "2019-11-10 16:09:07.993766\t策略初始化完成\n", + "2019-11-10 16:09:07.993766\t开始回放历史数据\n", + "2019-11-10 16:09:08.186919\t历史数据回放结束\n", + "2019-11-10 16:09:08.186919\t开始计算逐日盯市盈亏\n", + "2019-11-10 16:09:08.192772\t逐日盯市盈亏计算完成\n", + "2019-11-10 16:09:08.193748\t开始计算策略统计指标\n", + "2019-11-10 16:09:08.236670\t------------------------------\n", + "2019-11-10 16:09:08.236670\t首个交易日:\t2019-10-14\n", + "2019-11-10 16:09:08.236670\t最后交易日:\t2019-11-08\n", + "2019-11-10 16:09:08.236670\t总交易日:\t20\n", + "2019-11-10 16:09:08.236670\t盈利交易日:\t12\n", + "2019-11-10 16:09:08.236670\t亏损交易日:\t8\n", + "2019-11-10 16:09:08.236670\t起始资金:\t1,000,000.00\n", + "2019-11-10 16:09:08.236670\t结束资金:\t1,075,600.00\n", + "2019-11-10 16:09:08.236670\t总收益率:\t7.56%\n", + "2019-11-10 16:09:08.236670\t年化收益:\t90.72%\n", + "2019-11-10 16:09:08.236670\t最大回撤: \t-24,600.00\n", + "2019-11-10 16:09:08.236670\t百分比最大回撤: -2.29%\n", + "2019-11-10 16:09:08.236670\t最长回撤天数: \t9\n", + "2019-11-10 16:09:08.236670\t总盈亏:\t75,600.00\n", + "2019-11-10 16:09:08.236670\t总手续费:\t0.00\n", + "2019-11-10 16:09:08.236670\t总滑点:\t0.00\n", + "2019-11-10 16:09:08.236670\t总成交金额:\t3,603,600.00\n", + "2019-11-10 16:09:08.236670\t总成交笔数:\t435\n", + "2019-11-10 16:09:08.236670\t日均盈亏:\t3,780.00\n", + "2019-11-10 16:09:08.236670\t日均手续费:\t0.00\n", + "2019-11-10 16:09:08.236670\t日均滑点:\t0.00\n", + "2019-11-10 16:09:08.236670\t日均成交金额:\t180,180.00\n", + "2019-11-10 16:09:08.236670\t日均成交笔数:\t21.75\n", + "2019-11-10 16:09:08.236670\t日均收益率:\t0.27%\n", + "2019-11-10 16:09:08.236670\t收益标准差:\t0.81%\n", + "2019-11-10 16:09:08.236670\tSharpe Ratio:\t5.20\n", + "2019-11-10 16:09:08.236670\t收益回撤比:\t3.29\n" ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Github\\vnpy\\vnpy\\app\\spread_trading\\backtesting.py:289: FutureWarning: \n", + "The current behaviour of 'Series.argmax' is deprecated, use 'idxmax'\n", + "instead.\n", + "The behavior of 'argmax' will be corrected to return the positional\n", + "maximum in the future. For now, use 'series.values.argmax' or\n", + "'np.argmax(np.array(values))' to get the position of the maximum\n", + "row.\n", + " max_drawdown_start = df[\"balance\"][:max_drawdown_end].argmax()\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAnUAAAOSCAYAAAASwSbaAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvOIA7rQAAIABJREFUeJzs3Xl8lNXZ//HPZF9IAgkhCVtYAieBgCiyyaogi6Vq8We1tbVqK9q6PK1a9XFr7YNVq9W61RbcaOvSum8NIpsgLtiISmA4QNiXJJAA2deZ3x8zQEyBLCSZzOT7fr3yysw919xz3Qdmcs2573OOw+12IyIiIiL+LcjXCYiIiIjIqVNRJyIiIhIAVNSJiIiIBAAVdSIiIiIBQEWdiIiISABQUSciIiISAEJ8nYCISFswxvQDcoF13k3BQDlwk7V29Ume9wKQY619uK1zFBFpTSrqRCSQVVhrRxy5Y4z5PvACMMhnGYmItBEVdSLSmSQA+4wxQcCjwFggBnAAP2vYg2eMuQq4BggD4oEHrLVPG2OuAL4HuPAUiOXAT6y1TmNMMvAXIN37+F+stY8bY+KAx4BhQCiwFPi1tba2jY9ZRDoJXVMnIoEs0hjzlfdnB56i6n5gDNATGGetHQIsBG6v/0RjTBfgauA8a+3pwCXAH+qFTAZusNZmAp/Xe/6fgU3W2nRgHDDXGJOGp4jMttaOBE4HugM3tcVBi0jnpJ46EQlkDU+/TgPewtNbdhdwjTFmIDAFKKn/RGttqTFmNvAdY8wgYATQpV5ItrV2t/f2l8Ac7+1pwK3efRwGMr2vPRsYbYz5qTcusrUOUkQE1FMnIp2ItXYJsAWYBLzv3fw2ntOljvqxxpjewFdAKvAxniKwvop6t931nl/rvX9kPwOMMbF4BmpcbK0d4S00xwDXt8JhiYgAKupEpBMxxgwG+uE5lfqutfZp4D/AhXiKrvrOBPYD84DFwGzvPhrGNbQEuNIbG4fn2rlBwAfAr4wxDmNMOPAOKupEpBXp9KuIBLJIY8xX9e4HAXOBb4CXjTHr8HwOLgYu8g6gOGIxcBVg8Qx4+AhPkZfWyGteDzxtjPnG+3r3W2uzjTE34rmmbx2egRJL+PY1eiIip8ThdrsbjxIRERGRDk2nX0VEREQCgIo6ERERkQCgok5EREQkAKioExEREQkAKupEREREAkBATmny1VdfucPDw32dRodQVVWF2qJtqY3bntq4faidj1FbtD218THl5eUHRo4cmXiq+wnIoi48PJyMjAxfp9EhOJ1OtUUbUxu3PbVx+1A7H6O2aHtq42Oys7N3tMZ+dPpVREREJACoqBMREREJACrqRERERAJAQF5Tdzw1NTXs3r2byspKX6fSrmpqanA6nSd8PCIigt69exMaGtqOWYmIiEhr6zRF3e7du4mJiaFfv344HA5fp9NuKioqiIyMPO5jbrebwsJCdu/eTf/+/ds5MxEREWlNneb0a2VlJQkJCZ2qoGuMw+EgISGh0/VeioiIdBTVta5W21en6akDVNAdh9pERESkfR2uqGGpM5+snDxWbtrPS9/r0Sr77VRFna+98cYbbN26lVtuueWkcZ9//jmvvPIKjz76aDtlJiIiIm2psLSKDzd4CrlPcg9QU+cmJS6CH4zuC7TOGTMVdSIiIiJtIL+4kg/W55G1Lo/PtxXickPf+CiuGt+fmZnJnNa7K0FBDrKzs1vl9VTUtbOvvvqKn/zkJ5SWlnLDDTdQWVnJiy++ePTxxx577Fvx//jHP1i8eDG1tbXExMTwxBNP8N577/HRRx9RWVnJzp07ufrqq5kzZw5ff/019913H263m6SkJB5++GE2b97MQw89BEDXrl35/e9/T0xMTLses4iISGex+2A5i3LyyMrJ48udB3G7Ia1HF647O42ZmckMSYlts0ufOmVR93r2bv71n12tus/vn9mHi0b2bjQuMjKS+fPnU1RUxMUXX8z3v/995s+fT2RkJPfccw8ff/wxSUlJALhcLg4dOsQLL7xAUFAQP/3pT1m3bh0ApaWlPPvss2zfvp1rr72WOXPmcPfdd/Poo48ycOBAXnzxRXJzc/nd737HAw88QFpaGq+++irPPPMMv/rVr1r12EVERDqzrftLycrJY1FOHuv2HAYgIyWWX00bzKzMZAYltU9nSqcs6nxp5MiRR0edxsTEEBISwm233UZ0dDRbt25lxIgRR2ODgoIIDQ3lpptuIioqiry8PGprawFIT08HICUlherqagAKCwsZOHAgAJdddhkA27Zt49577wU8c9Zp6hIREZFT43a7sfklZK3zFHI2vwSA0/p05fZZ6czKTCY1Ibrd8+qURd1FI3s3qVetLRzpadu/fz8lJSUsXLiQ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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" } ], "source": [ diff --git a/vnpy/app/spread_trading/backtesting.py b/vnpy/app/spread_trading/backtesting.py index 20dd8419..b87f53c9 100644 --- a/vnpy/app/spread_trading/backtesting.py +++ b/vnpy/app/spread_trading/backtesting.py @@ -1,5 +1,5 @@ from collections import defaultdict -from datetime import date, datetime, timedelta +from datetime import date, datetime from typing import Callable, Type, Dict, List from functools import lru_cache @@ -756,4 +756,3 @@ def load_tick_data( return database_manager.load_tick_data( spread.name, Exchange.LOCAL, start, end ) - From c5ee08ae8284cb30739cff0ab55b22ef556ea3f7 Mon Sep 17 00:00:00 2001 From: "vn.py" Date: Sun, 10 Nov 2019 16:34:39 +0800 Subject: [PATCH 5/5] [Add] load_bar/load_tick function for SpreadStrategyEngine --- examples/spread_backtesting/backtesting.ipynb | 518 ++++++++++++++++-- vnpy/app/cta_strategy/engine.py | 2 - vnpy/app/spread_trading/backtesting.py | 77 +-- vnpy/app/spread_trading/base.py | 80 ++- vnpy/app/spread_trading/engine.py | 30 +- 5 files changed, 587 insertions(+), 120 deletions(-) diff --git a/examples/spread_backtesting/backtesting.ipynb b/examples/spread_backtesting/backtesting.ipynb index c914ecc6..459ea279 100644 --- a/examples/spread_backtesting/backtesting.ipynb +++ b/examples/spread_backtesting/backtesting.ipynb @@ -65,48 +65,48 @@ "name": "stdout", "output_type": "stream", "text": [ - "2019-11-10 16:09:03.822440\t开始加载历史数据\n", - "2019-11-10 16:09:07.915724\t历史数据加载完成,数据量:7200\n", - "2019-11-10 16:09:07.993766\t策略初始化完成\n", - "2019-11-10 16:09:07.993766\t开始回放历史数据\n", - "2019-11-10 16:09:08.186919\t历史数据回放结束\n", - "2019-11-10 16:09:08.186919\t开始计算逐日盯市盈亏\n", - "2019-11-10 16:09:08.192772\t逐日盯市盈亏计算完成\n", - "2019-11-10 16:09:08.193748\t开始计算策略统计指标\n", - "2019-11-10 16:09:08.236670\t------------------------------\n", - "2019-11-10 16:09:08.236670\t首个交易日:\t2019-10-14\n", - "2019-11-10 16:09:08.236670\t最后交易日:\t2019-11-08\n", - "2019-11-10 16:09:08.236670\t总交易日:\t20\n", - "2019-11-10 16:09:08.236670\t盈利交易日:\t12\n", - "2019-11-10 16:09:08.236670\t亏损交易日:\t8\n", - "2019-11-10 16:09:08.236670\t起始资金:\t1,000,000.00\n", - "2019-11-10 16:09:08.236670\t结束资金:\t1,075,600.00\n", - "2019-11-10 16:09:08.236670\t总收益率:\t7.56%\n", - "2019-11-10 16:09:08.236670\t年化收益:\t90.72%\n", - "2019-11-10 16:09:08.236670\t最大回撤: \t-24,600.00\n", - "2019-11-10 16:09:08.236670\t百分比最大回撤: -2.29%\n", - "2019-11-10 16:09:08.236670\t最长回撤天数: \t9\n", - "2019-11-10 16:09:08.236670\t总盈亏:\t75,600.00\n", - "2019-11-10 16:09:08.236670\t总手续费:\t0.00\n", - "2019-11-10 16:09:08.236670\t总滑点:\t0.00\n", - "2019-11-10 16:09:08.236670\t总成交金额:\t3,603,600.00\n", - "2019-11-10 16:09:08.236670\t总成交笔数:\t435\n", - "2019-11-10 16:09:08.236670\t日均盈亏:\t3,780.00\n", - "2019-11-10 16:09:08.236670\t日均手续费:\t0.00\n", - "2019-11-10 16:09:08.236670\t日均滑点:\t0.00\n", - "2019-11-10 16:09:08.236670\t日均成交金额:\t180,180.00\n", - "2019-11-10 16:09:08.236670\t日均成交笔数:\t21.75\n", - "2019-11-10 16:09:08.236670\t日均收益率:\t0.27%\n", - "2019-11-10 16:09:08.236670\t收益标准差:\t0.81%\n", - "2019-11-10 16:09:08.236670\tSharpe Ratio:\t5.20\n", - "2019-11-10 16:09:08.236670\t收益回撤比:\t3.29\n" + "2019-11-10 16:33:53.249984\t开始加载历史数据\n", + "2019-11-10 16:33:56.769516\t历史数据加载完成,数据量:7200\n", + "2019-11-10 16:33:56.816391\t策略初始化完成\n", + "2019-11-10 16:33:56.816391\t开始回放历史数据\n", + "2019-11-10 16:33:56.957016\t历史数据回放结束\n", + "2019-11-10 16:33:56.957016\t开始计算逐日盯市盈亏\n", + "2019-11-10 16:33:56.957016\t逐日盯市盈亏计算完成\n", + "2019-11-10 16:33:56.957016\t开始计算策略统计指标\n", + "2019-11-10 16:33:56.990219\t------------------------------\n", + "2019-11-10 16:33:56.991195\t首个交易日:\t2019-10-14\n", + "2019-11-10 16:33:56.991195\t最后交易日:\t2019-11-08\n", + "2019-11-10 16:33:56.992172\t总交易日:\t20\n", + "2019-11-10 16:33:56.992172\t盈利交易日:\t12\n", + "2019-11-10 16:33:56.992172\t亏损交易日:\t8\n", + "2019-11-10 16:33:56.993148\t起始资金:\t1,000,000.00\n", + "2019-11-10 16:33:56.993148\t结束资金:\t1,075,600.00\n", + "2019-11-10 16:33:56.994125\t总收益率:\t7.56%\n", + "2019-11-10 16:33:56.994125\t年化收益:\t90.72%\n", + "2019-11-10 16:33:56.994125\t最大回撤: \t-24,600.00\n", + "2019-11-10 16:33:56.995102\t百分比最大回撤: -2.29%\n", + "2019-11-10 16:33:56.995102\t最长回撤天数: \t9\n", + "2019-11-10 16:33:56.996078\t总盈亏:\t75,600.00\n", + "2019-11-10 16:33:56.996078\t总手续费:\t0.00\n", + "2019-11-10 16:33:56.996078\t总滑点:\t0.00\n", + "2019-11-10 16:33:56.997055\t总成交金额:\t3,603,600.00\n", + "2019-11-10 16:33:56.997055\t总成交笔数:\t435\n", + "2019-11-10 16:33:56.997055\t日均盈亏:\t3,780.00\n", + "2019-11-10 16:33:56.998031\t日均手续费:\t0.00\n", + "2019-11-10 16:33:56.999008\t日均滑点:\t0.00\n", + "2019-11-10 16:33:56.999008\t日均成交金额:\t180,180.00\n", + "2019-11-10 16:33:56.999008\t日均成交笔数:\t21.75\n", + "2019-11-10 16:33:56.999008\t日均收益率:\t0.27%\n", + "2019-11-10 16:33:56.999008\t收益标准差:\t0.81%\n", + "2019-11-10 16:33:56.999008\tSharpe Ratio:\t5.20\n", + "2019-11-10 16:33:56.999008\t收益回撤比:\t3.29\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "C:\\Github\\vnpy\\vnpy\\app\\spread_trading\\backtesting.py:289: FutureWarning: \n", + "C:\\Github\\vnpy\\vnpy\\app\\spread_trading\\backtesting.py:286: FutureWarning: \n", "The current behaviour of 'Series.argmax' is deprecated, use 'idxmax'\n", "instead.\n", "The behavior of 'argmax' will be corrected to return the positional\n", @@ -140,9 +140,451 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='1', tradeid='1', direction=, offset=, price=5.0, volume=10, time='09:31:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='2', tradeid='2', direction=, offset=, price=2.0, volume=10, time='09:36:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='3', tradeid='3', direction=, offset=, price=0.2, volume=10, time='09:54:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='4', tradeid='4', direction=, offset=, price=2.4, volume=10, time='10:01:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='5', tradeid='5', direction=, offset=, price=0.2, volume=10, time='10:02:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='6', tradeid='6', direction=, offset=, price=0.2, volume=10, time='10:06:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='7', tradeid='7', direction=, offset=, price=0.2, volume=10, time='10:06:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='8', tradeid='8', direction=, offset=, price=0.2, volume=10, time='10:06:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='9', tradeid='9', direction=, offset=, price=0.6, volume=20, time='10:13:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='10', tradeid='10', direction=, offset=, price=0.2, volume=10, time='10:29:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='11', tradeid='11', direction=, offset=, price=0.2, volume=10, time='10:43:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='12', tradeid='12', direction=, offset=, price=0.2, volume=10, time='10:43:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='13', tradeid='13', direction=, offset=, price=0.2, volume=10, time='10:43:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='14', tradeid='14', direction=, offset=, price=0.2, volume=10, time='10:43:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='15', tradeid='15', direction=, offset=, price=0.2, volume=10, time='10:43:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='16', tradeid='16', direction=, offset=, price=0.2, volume=10, time='10:43:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='17', tradeid='17', direction=, offset=, price=0.2, volume=10, time='10:43:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='18', tradeid='18', direction=, offset=, price=0.8, volume=60, time='10:47:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='19', tradeid='19', direction=, offset=, price=0.8, volume=10, time='10:56:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='20', tradeid='20', direction=, offset=, price=0.6, volume=10, time='10:58:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='21', tradeid='21', direction=, offset=, price=0.6, volume=10, time='10:58:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='22', tradeid='22', direction=, offset=, price=0.4, volume=10, time='11:07:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='23', tradeid='23', direction=, offset=, price=0.4, volume=10, time='11:07:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='24', tradeid='24', direction=, offset=, price=0.4, volume=10, time='11:07:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='25', tradeid='25', direction=, offset=, price=1.8, volume=20, time='11:08:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='26', tradeid='26', direction=, offset=, price=0.4, volume=10, time='13:23:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='27', tradeid='27', direction=, offset=, price=0.2, volume=10, time='13:26:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='28', tradeid='28', direction=, offset=, price=0.4, volume=10, time='13:43:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='29', tradeid='29', direction=, offset=, price=0.6, volume=10, time='13:47:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='30', tradeid='30', direction=, offset=, price=1.6, volume=10, time='14:02:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='31', tradeid='31', direction=, offset=, price=0.6, volume=10, time='14:09:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='32', tradeid='32', direction=, offset=, price=1.2, volume=10, time='14:13:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='33', tradeid='33', direction=, offset=, price=1.0, volume=10, time='14:20:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='34', tradeid='34', direction=, offset=, price=1.0, volume=10, time='14:41:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='35', tradeid='35', direction=, offset=, price=0.8, volume=10, time='14:50:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='36', tradeid='36', direction=, offset=, price=3.0, volume=10, time='14:59:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='37', tradeid='37', direction=, offset=, price=0.2, volume=10, time='09:35:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='38', tradeid='38', direction=, offset=, price=0.2, volume=10, time='09:35:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='39', tradeid='39', direction=, offset=, price=0.2, volume=10, time='09:35:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='40', tradeid='40', direction=, offset=, price=0.2, volume=10, time='09:35:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='41', tradeid='41', direction=, offset=, price=0.2, volume=10, time='09:35:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='42', tradeid='42', direction=, offset=, price=0.6, volume=40, time='09:38:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='43', tradeid='43', direction=, offset=, price=2.8, volume=10, time='09:58:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='44', tradeid='44', direction=, offset=, price=2.0, volume=10, time='10:02:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='45', tradeid='45', direction=, offset=, price=2.6, volume=10, time='10:06:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='46', tradeid='46', direction=, offset=, price=2.2, volume=10, time='10:07:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='47', tradeid='47', direction=, offset=, price=0.8, volume=10, time='10:28:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='48', tradeid='48', direction=, offset=, price=1.8, volume=10, time='10:30:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='49', tradeid='49', direction=, offset=, price=1.2, volume=10, time='10:41:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='50', tradeid='50', direction=, offset=, price=2.6, volume=10, time='10:48:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='51', tradeid='51', direction=, offset=, price=0.4, volume=10, time='11:02:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='52', tradeid='52', direction=, offset=, price=1.2, volume=10, time='11:04:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='53', tradeid='53', direction=, offset=, price=1.4, volume=10, time='11:11:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='54', tradeid='54', direction=, offset=, price=0.4, volume=10, time='11:13:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='55', tradeid='55', direction=, offset=, price=0.4, volume=10, time='11:19:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='56', tradeid='56', direction=, offset=, price=1.2, volume=10, time='11:23:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='57', tradeid='57', direction=, offset=, price=0.8, volume=10, time='13:21:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='58', tradeid='58', direction=, offset=, price=1.4, volume=10, time='13:31:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='59', tradeid='59', direction=, offset=, price=0.2, volume=10, time='13:49:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='60', tradeid='60', direction=, offset=, price=0.6, volume=10, time='13:54:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='61', tradeid='61', direction=, offset=, price=0.8, volume=10, time='14:14:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='62', tradeid='62', direction=, offset=, price=1.6, volume=10, time='14:19:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='63', tradeid='63', direction=, offset=, price=2.2, volume=10, time='14:20:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='64', tradeid='64', direction=, offset=, price=1.2, volume=10, time='14:23:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='65', tradeid='65', direction=, offset=, price=0.8, volume=10, time='09:31:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='66', tradeid='66', direction=, offset=, price=0.8, volume=10, time='09:45:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='67', tradeid='67', direction=, offset=, price=1.8, volume=10, time='09:56:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='68', tradeid='68', direction=, offset=, price=0.2, volume=10, time='10:03:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='69', tradeid='69', direction=, offset=, price=2.8, volume=10, time='10:08:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='70', tradeid='70', direction=, offset=, price=0.8, volume=10, time='10:16:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='71', tradeid='71', direction=, offset=, price=0.6, volume=10, time='10:43:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='72', tradeid='72', direction=, offset=, price=0.8, volume=10, time='10:44:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='73', tradeid='73', direction=, offset=, price=2.0, volume=10, time='11:04:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='74', tradeid='74', direction=, offset=, price=1.2, volume=10, time='11:05:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='75', tradeid='75', direction=, offset=, price=0.6, volume=10, time='13:00:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='76', tradeid='76', direction=, offset=, price=1.4, volume=10, time='13:04:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='77', tradeid='77', direction=, offset=, price=2.8, volume=10, time='13:32:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='78', tradeid='78', direction=, offset=, price=0.8, volume=10, time='13:36:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='79', tradeid='79', direction=, offset=, price=0.8, volume=10, time='13:53:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='80', tradeid='80', direction=, offset=, price=2.4, volume=10, time='13:54:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='81', tradeid='81', direction=, offset=, price=3.6, volume=10, time='14:22:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='82', tradeid='82', direction=, offset=, price=4.4, volume=10, time='14:34:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='83', tradeid='83', direction=, offset=, price=3.0, volume=10, time='14:43:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='84', tradeid='84', direction=, offset=, price=2.8, volume=10, time='14:48:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='85', tradeid='85', direction=, offset=, price=3.6, volume=10, time='14:56:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='86', tradeid='86', direction=, offset=, price=2.8, volume=10, time='09:30:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='87', tradeid='87', direction=, offset=, price=3.2, volume=10, time='10:04:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='88', tradeid='88', direction=, offset=, price=4.0, volume=10, time='10:18:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='89', tradeid='89', direction=, offset=, price=5.0, volume=10, time='10:24:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='90', tradeid='90', direction=, offset=, price=5.2, volume=10, time='10:28:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='91', tradeid='91', direction=, offset=, price=4.6, volume=10, time='11:12:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='92', tradeid='92', direction=, offset=, price=5.6, volume=10, time='11:13:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='93', tradeid='93', direction=, offset=, price=5.6, volume=10, time='13:26:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='94', tradeid='94', direction=, offset=, price=6.2, volume=10, time='13:27:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='95', tradeid='95', direction=, offset=, price=5.0, volume=10, time='13:28:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='96', tradeid='96', direction=, offset=, price=4.8, volume=10, time='13:29:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='97', tradeid='97', direction=, offset=, price=4.2, volume=10, time='13:32:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='98', tradeid='98', direction=, offset=, price=5.6, volume=10, time='13:36:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='99', tradeid='99', direction=, offset=, price=5.8, volume=10, time='13:59:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='100', tradeid='100', direction=, offset=, price=6.0, volume=10, time='14:05:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='101', tradeid='101', direction=, offset=, price=4.6, volume=10, time='14:14:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='102', tradeid='102', direction=, offset=, price=5.0, volume=10, time='14:16:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='103', tradeid='103', direction=, offset=, price=7.0, volume=10, time='14:22:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='104', tradeid='104', direction=, offset=, price=6.6, volume=10, time='14:33:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='105', tradeid='105', direction=, offset=, price=5.6, volume=10, time='14:56:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='106', tradeid='106', direction=, offset=, price=4.0, volume=10, time='09:38:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='107', tradeid='107', direction=, offset=, price=3.8, volume=10, time='09:46:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='108', tradeid='108', direction=, offset=, price=3.6, volume=10, time='09:49:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='109', tradeid='109', direction=, offset=, price=3.4, volume=10, time='10:23:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='110', tradeid='110', direction=, offset=, price=3.8, volume=10, time='10:33:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='111', tradeid='111', direction=, offset=, price=2.6, volume=10, time='10:43:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='112', tradeid='112', direction=, offset=, price=4.0, volume=10, time='10:48:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='113', tradeid='113', direction=, offset=, price=3.4, volume=10, time='11:11:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='114', tradeid='114', direction=, offset=, price=3.4, volume=10, time='11:17:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='115', tradeid='115', direction=, offset=, price=4.0, volume=10, time='11:23:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='116', tradeid='116', direction=, offset=, price=5.2, volume=10, time='13:00:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='117', tradeid='117', direction=, offset=, price=4.6, volume=10, time='13:18:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='118', tradeid='118', direction=, offset=, price=5.0, volume=10, time='13:21:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='119', tradeid='119', direction=, offset=, price=6.0, volume=10, time='13:33:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='120', tradeid='120', direction=, offset=, price=5.8, volume=10, time='13:38:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='121', tradeid='121', direction=, offset=, price=5.4, volume=10, time='13:52:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='122', tradeid='122', direction=, offset=, price=4.8, volume=10, time='13:53:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='123', tradeid='123', direction=, offset=, price=4.8, volume=10, time='14:55:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='124', tradeid='124', direction=, offset=, price=4.2, volume=10, time='14:56:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='125', tradeid='125', direction=, offset=, price=6.6, volume=10, time='09:30:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='126', tradeid='126', direction=, offset=, price=5.8, volume=10, time='09:34:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='127', tradeid='127', direction=, offset=, price=4.8, volume=10, time='09:46:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='128', tradeid='128', direction=, offset=, price=5.6, volume=10, time='09:53:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='129', tradeid='129', direction=, offset=, price=5.2, volume=10, time='10:07:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='130', tradeid='130', direction=, offset=, price=5.0, volume=10, time='10:08:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='131', tradeid='131', direction=, offset=, price=6.4, volume=10, time='10:11:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='132', tradeid='132', direction=, offset=, price=5.8, volume=10, time='10:18:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='133', tradeid='133', direction=, offset=, price=6.8, volume=10, time='11:15:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='134', tradeid='134', direction=, offset=, price=5.8, volume=10, time='11:16:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='135', tradeid='135', direction=, offset=, price=6.4, volume=10, time='13:10:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='136', tradeid='136', direction=, offset=, price=5.6, volume=10, time='13:14:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='137', tradeid='137', direction=, offset=, price=6.0, volume=10, time='13:25:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='138', tradeid='138', direction=, offset=, price=6.6, volume=10, time='13:26:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='139', tradeid='139', direction=, offset=, price=4.8, volume=10, time='14:08:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='140', tradeid='140', direction=, offset=, price=5.2, volume=10, time='14:10:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='141', tradeid='141', direction=, offset=, price=5.6, volume=10, time='14:13:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='142', tradeid='142', direction=, offset=, price=5.6, volume=10, time='14:25:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='143', tradeid='143', direction=, offset=, price=5.2, volume=10, time='14:30:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='144', tradeid='144', direction=, offset=, price=4.8, volume=10, time='14:33:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='145', tradeid='145', direction=, offset=, price=5.6, volume=10, time='14:50:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='146', tradeid='146', direction=, offset=, price=5.6, volume=10, time='14:52:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='147', tradeid='147', direction=, offset=, price=5.4, volume=10, time='14:59:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='148', tradeid='148', direction=, offset=, price=5.2, volume=10, time='09:30:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='149', tradeid='149', direction=, offset=, price=6.2, volume=10, time='09:33:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='150', tradeid='150', direction=, offset=, price=4.4, volume=10, time='09:35:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='151', tradeid='151', direction=, offset=, price=4.6, volume=10, time='09:36:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='152', tradeid='152', direction=, offset=, price=4.6, volume=10, time='09:51:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='153', tradeid='153', direction=, offset=, price=4.8, volume=10, time='10:07:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='154', tradeid='154', direction=, offset=, price=4.2, volume=10, time='10:10:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='155', tradeid='155', direction=, offset=, price=4.4, volume=10, time='10:39:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='156', tradeid='156', direction=, offset=, price=4.4, volume=10, time='10:42:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='157', tradeid='157', direction=, offset=, price=5.4, volume=10, time='10:45:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='158', tradeid='158', direction=, offset=, price=4.4, volume=10, time='10:46:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='159', tradeid='159', direction=, offset=, price=5.0, volume=10, time='11:24:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='160', tradeid='160', direction=, offset=, price=5.2, volume=10, time='11:27:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='161', tradeid='161', direction=, offset=, price=5.2, volume=10, time='13:42:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='162', tradeid='162', direction=, offset=, price=4.8, volume=10, time='13:44:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='163', tradeid='163', direction=, offset=, price=4.8, volume=10, time='13:47:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='164', tradeid='164', direction=, offset=, price=4.8, volume=10, time='13:49:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='165', tradeid='165', direction=, offset=, price=2.2, volume=10, time='14:30:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='166', tradeid='166', direction=, offset=, price=4.6, volume=10, time='14:36:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='167', tradeid='167', direction=, offset=, price=3.4, volume=10, time='14:55:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='168', tradeid='168', direction=, offset=, price=4.2, volume=10, time='14:58:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='169', tradeid='169', direction=, offset=, price=3.2, volume=10, time='10:19:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='170', tradeid='170', direction=, offset=, price=3.6, volume=10, time='10:27:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='171', tradeid='171', direction=, offset=, price=3.6, volume=10, time='11:01:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='172', tradeid='172', direction=, offset=, price=3.6, volume=10, time='11:02:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='173', tradeid='173', direction=, offset=, price=3.4, volume=10, time='11:21:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='174', tradeid='174', direction=, offset=, price=4.0, volume=10, time='11:23:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='175', tradeid='175', direction=, offset=, price=4.0, volume=10, time='13:15:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='176', tradeid='176', direction=, offset=, price=3.8, volume=10, time='13:16:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='177', tradeid='177', direction=, offset=, price=4.4, volume=10, time='13:20:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='178', tradeid='178', direction=, offset=, price=4.8, volume=10, time='13:24:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='179', tradeid='179', direction=, offset=, price=4.6, volume=10, time='13:36:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='180', tradeid='180', direction=, offset=, price=3.4, volume=10, time='13:37:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='181', tradeid='181', direction=, offset=, price=3.6, volume=10, time='13:49:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='182', tradeid='182', direction=, offset=, price=3.4, volume=10, time='13:51:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='183', tradeid='183', direction=, offset=, price=5.0, volume=10, time='13:55:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='184', tradeid='184', direction=, offset=, price=3.6, volume=10, time='13:58:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='185', tradeid='185', direction=, offset=, price=3.4, volume=10, time='14:01:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='186', tradeid='186', direction=, offset=, price=4.4, volume=10, time='14:06:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='187', tradeid='187', direction=, offset=, price=3.6, volume=10, time='14:18:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='188', tradeid='188', direction=, offset=, price=4.2, volume=10, time='14:21:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='189', tradeid='189', direction=, offset=, price=4.2, volume=10, time='09:30:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='190', tradeid='190', direction=, offset=, price=3.4, volume=10, time='09:31:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='191', tradeid='191', direction=, offset=, price=4.8, volume=10, time='10:21:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='192', tradeid='192', direction=, offset=, price=4.0, volume=10, time='10:24:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='193', tradeid='193', direction=, offset=, price=4.6, volume=10, time='10:26:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='194', tradeid='194', direction=, offset=, price=4.2, volume=10, time='10:28:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='195', tradeid='195', direction=, offset=, price=3.6, volume=10, time='10:50:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='196', tradeid='196', direction=, offset=, price=3.4, volume=10, time='10:53:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='197', tradeid='197', direction=, offset=, price=3.6, volume=10, time='11:23:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='198', tradeid='198', direction=, offset=, price=4.2, volume=10, time='11:25:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='199', tradeid='199', direction=, offset=, price=4.6, volume=10, time='11:27:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='200', tradeid='200', direction=, offset=, price=3.8, volume=10, time='11:28:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='201', tradeid='201', direction=, offset=, price=3.8, volume=10, time='13:03:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='202', tradeid='202', direction=, offset=, price=3.4, volume=10, time='13:08:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='203', tradeid='203', direction=, offset=, price=4.0, volume=10, time='13:52:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='204', tradeid='204', direction=, offset=, price=3.0, volume=10, time='13:53:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='205', tradeid='205', direction=, offset=, price=3.6, volume=10, time='09:35:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='206', tradeid='206', direction=, offset=, price=2.8, volume=10, time='09:38:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='207', tradeid='207', direction=, offset=, price=2.8, volume=10, time='09:44:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='208', tradeid='208', direction=, offset=, price=2.6, volume=10, time='09:47:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='209', tradeid='209', direction=, offset=, price=2.2, volume=10, time='10:01:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='210', tradeid='210', direction=, offset=, price=2.0, volume=10, time='10:03:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='211', tradeid='211', direction=, offset=, price=2.0, volume=10, time='10:12:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='212', tradeid='212', direction=, offset=, price=1.6, volume=10, time='10:15:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='213', tradeid='213', direction=, offset=, price=3.4, volume=10, time='10:22:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='214', tradeid='214', direction=, offset=, price=3.6, volume=10, time='10:26:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='215', tradeid='215', direction=, offset=, price=2.4, volume=10, time='10:36:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='216', tradeid='216', direction=, offset=, price=2.2, volume=10, time='10:38:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='217', tradeid='217', direction=, offset=, price=2.4, volume=10, time='10:41:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='218', tradeid='218', direction=, offset=, price=1.8, volume=10, time='10:48:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='219', tradeid='219', direction=, offset=, price=3.0, volume=10, time='11:15:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='220', tradeid='220', direction=, offset=, price=3.2, volume=10, time='11:16:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='221', tradeid='221', direction=, offset=, price=2.6, volume=10, time='13:00:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='222', tradeid='222', direction=, offset=, price=2.6, volume=10, time='13:12:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='223', tradeid='223', direction=, offset=, price=2.4, volume=10, time='13:20:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='224', tradeid='224', direction=, offset=, price=3.0, volume=10, time='13:22:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='225', tradeid='225', direction=, offset=, price=2.2, volume=10, time='13:30:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='226', tradeid='226', direction=, offset=, price=2.8, volume=10, time='13:31:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='227', tradeid='227', direction=, offset=, price=3.6, volume=10, time='13:33:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='228', tradeid='228', direction=, offset=, price=3.6, volume=10, time='13:38:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='229', tradeid='229', direction=, offset=, price=5.2, volume=10, time='14:07:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='230', tradeid='230', direction=, offset=, price=3.6, volume=10, time='14:09:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='231', tradeid='231', direction=, offset=, price=1.8, volume=10, time='09:31:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='232', tradeid='232', direction=, offset=, price=1.8, volume=10, time='09:31:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='233', tradeid='233', direction=, offset=, price=0.8, volume=20, time='09:44:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='234', tradeid='234', direction=, offset=, price=0.4, volume=10, time='09:50:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='235', tradeid='235', direction=, offset=, price=0.6, volume=10, time='10:01:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='236', tradeid='236', direction=, offset=, price=0.4, volume=10, time='10:13:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='237', tradeid='237', direction=, offset=, price=1.2, volume=10, time='10:15:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='238', tradeid='238', direction=, offset=, price=2.0, volume=10, time='10:39:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='239', tradeid='239', direction=, offset=, price=1.0, volume=10, time='10:41:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='240', tradeid='240', direction=, offset=, price=2.8, volume=10, time='10:52:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='241', tradeid='241', direction=, offset=, price=1.6, volume=10, time='10:54:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='242', tradeid='242', direction=, offset=, price=3.0, volume=10, time='11:26:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='243', tradeid='243', direction=, offset=, price=3.2, volume=10, time='11:28:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='244', tradeid='244', direction=, offset=, price=1.4, volume=10, time='13:07:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='245', tradeid='245', direction=, offset=, price=2.8, volume=10, time='13:09:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='246', tradeid='246', direction=, offset=, price=2.6, volume=10, time='13:40:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='247', tradeid='247', direction=, offset=, price=1.6, volume=10, time='13:41:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='248', tradeid='248', direction=, offset=, price=1.4, volume=10, time='13:46:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='249', tradeid='249', direction=, offset=, price=2.6, volume=10, time='13:49:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='250', tradeid='250', direction=, offset=, price=3.0, volume=10, time='13:52:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='251', tradeid='251', direction=, offset=, price=3.4, volume=10, time='13:55:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='252', tradeid='252', direction=, offset=, price=1.8, volume=10, time='14:23:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='253', tradeid='253', direction=, offset=, price=2.6, volume=10, time='14:24:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='254', tradeid='254', direction=, offset=, price=1.0, volume=10, time='09:36:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='255', tradeid='255', direction=, offset=, price=2.2, volume=10, time='09:37:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='256', tradeid='256', direction=, offset=, price=2.2, volume=10, time='09:56:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='257', tradeid='257', direction=, offset=, price=1.8, volume=10, time='09:59:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='258', tradeid='258', direction=, offset=, price=2.4, volume=10, time='10:16:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='259', tradeid='259', direction=, offset=, price=2.6, volume=10, time='10:19:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='260', tradeid='260', direction=, offset=, price=3.0, volume=10, time='10:55:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='261', tradeid='261', direction=, offset=, price=2.8, volume=10, time='10:56:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='262', tradeid='262', direction=, offset=, price=2.0, volume=10, time='11:11:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='263', tradeid='263', direction=, offset=, price=1.6, volume=10, time='11:15:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='264', tradeid='264', direction=, offset=, price=2.4, volume=10, time='11:16:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='265', tradeid='265', direction=, offset=, price=2.8, volume=10, time='11:17:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='266', tradeid='266', direction=, offset=, price=2.8, volume=10, time='11:19:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='267', tradeid='267', direction=, offset=, price=2.0, volume=10, time='11:21:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='268', tradeid='268', direction=, offset=, price=2.8, volume=10, time='13:31:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='269', tradeid='269', direction=, offset=, price=3.0, volume=10, time='13:36:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='270', tradeid='270', direction=, offset=, price=2.4, volume=10, time='14:12:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='271', tradeid='271', direction=, offset=, price=2.0, volume=10, time='14:15:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='272', tradeid='272', direction=, offset=, price=2.8, volume=10, time='14:41:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='273', tradeid='273', direction=, offset=, price=2.6, volume=10, time='14:43:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='274', tradeid='274', direction=, offset=, price=2.6, volume=10, time='09:33:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='275', tradeid='275', direction=, offset=, price=2.0, volume=10, time='09:34:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='276', tradeid='276', direction=, offset=, price=3.8, volume=10, time='09:41:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='277', tradeid='277', direction=, offset=, price=3.6, volume=10, time='09:51:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='278', tradeid='278', direction=, offset=, price=3.4, volume=10, time='10:06:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='279', tradeid='279', direction=, offset=, price=2.8, volume=10, time='10:07:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='280', tradeid='280', direction=, offset=, price=3.6, volume=10, time='10:16:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='281', tradeid='281', direction=, offset=, price=3.2, volume=10, time='10:20:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='282', tradeid='282', direction=, offset=, price=4.6, volume=10, time='10:32:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='283', tradeid='283', direction=, offset=, price=4.2, volume=10, time='10:42:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='284', tradeid='284', direction=, offset=, price=3.4, volume=10, time='10:53:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='285', tradeid='285', direction=, offset=, price=2.8, volume=10, time='11:09:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='286', tradeid='286', direction=, offset=, price=3.2, volume=10, time='11:25:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='287', tradeid='287', direction=, offset=, price=4.6, volume=10, time='11:26:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='288', tradeid='288', direction=, offset=, price=3.8, volume=10, time='11:27:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='289', tradeid='289', direction=, offset=, price=3.4, volume=10, time='13:01:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='290', tradeid='290', direction=, offset=, price=4.0, volume=10, time='13:16:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='291', tradeid='291', direction=, offset=, price=3.6, volume=10, time='13:28:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='292', tradeid='292', direction=, offset=, price=4.2, volume=10, time='13:41:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='293', tradeid='293', direction=, offset=, price=4.2, volume=10, time='13:48:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='294', tradeid='294', direction=, offset=, price=4.6, volume=10, time='14:47:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='295', tradeid='295', direction=, offset=, price=4.4, volume=10, time='14:56:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='296', tradeid='296', direction=, offset=, price=3.6, volume=10, time='10:55:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='297', tradeid='297', direction=, offset=, price=4.2, volume=10, time='10:56:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='298', tradeid='298', direction=, offset=, price=4.2, volume=10, time='11:15:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='299', tradeid='299', direction=, offset=, price=4.2, volume=10, time='11:19:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='300', tradeid='300', direction=, offset=, price=4.0, volume=10, time='13:24:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='301', tradeid='301', direction=, offset=, price=3.6, volume=10, time='13:25:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='302', tradeid='302', direction=, offset=, price=3.6, volume=10, time='13:36:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='303', tradeid='303', direction=, offset=, price=4.4, volume=10, time='13:38:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='304', tradeid='304', direction=, offset=, price=3.8, volume=10, time='13:47:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='305', tradeid='305', direction=, offset=, price=3.2, volume=10, time='13:50:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='306', tradeid='306', direction=, offset=, price=2.8, volume=10, time='14:32:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='307', tradeid='307', direction=, offset=, price=2.8, volume=10, time='14:34:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='308', tradeid='308', direction=, offset=, price=4.6, volume=10, time='14:40:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='309', tradeid='309', direction=, offset=, price=4.2, volume=10, time='14:49:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='310', tradeid='310', direction=, offset=, price=3.0, volume=10, time='09:30:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='311', tradeid='311', direction=, offset=, price=4.0, volume=10, time='09:31:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='312', tradeid='312', direction=, offset=, price=3.6, volume=10, time='09:40:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='313', tradeid='313', direction=, offset=, price=4.2, volume=10, time='09:41:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='314', tradeid='314', direction=, offset=, price=4.2, volume=10, time='10:09:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='315', tradeid='315', direction=, offset=, price=2.2, volume=10, time='10:15:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='316', tradeid='316', direction=, offset=, price=3.6, volume=10, time='10:16:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='317', tradeid='317', direction=, offset=, price=4.2, volume=10, time='10:18:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='318', tradeid='318', direction=, offset=, price=4.2, volume=10, time='11:15:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='319', tradeid='319', direction=, offset=, price=4.2, volume=10, time='11:17:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='320', tradeid='320', direction=, offset=, price=4.0, volume=10, time='11:21:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='321', tradeid='321', direction=, offset=, price=3.6, volume=10, time='11:22:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='322', tradeid='322', direction=, offset=, price=4.0, volume=10, time='11:24:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='323', tradeid='323', direction=, offset=, price=3.6, volume=10, time='11:25:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='324', tradeid='324', direction=, offset=, price=2.8, volume=10, time='13:13:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='325', tradeid='325', direction=, offset=, price=2.4, volume=10, time='13:32:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='326', tradeid='326', direction=, offset=, price=2.2, volume=10, time='13:54:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='327', tradeid='327', direction=, offset=, price=3.0, volume=10, time='13:58:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='328', tradeid='328', direction=, offset=, price=2.2, volume=10, time='14:14:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='329', tradeid='329', direction=, offset=, price=3.2, volume=10, time='14:17:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='330', tradeid='330', direction=, offset=, price=3.2, volume=10, time='14:34:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='331', tradeid='331', direction=, offset=, price=1.6, volume=10, time='14:35:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='332', tradeid='332', direction=, offset=, price=3.2, volume=10, time='14:54:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='333', tradeid='333', direction=, offset=, price=1.4, volume=10, time='14:55:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='334', tradeid='334', direction=, offset=, price=2.6, volume=10, time='09:32:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='335', tradeid='335', direction=, offset=, price=1.6, volume=10, time='09:33:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='336', tradeid='336', direction=, offset=, price=2.6, volume=10, time='09:50:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='337', tradeid='337', direction=, offset=, price=2.2, volume=10, time='09:53:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='338', tradeid='338', direction=, offset=, price=2.0, volume=10, time='10:39:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='339', tradeid='339', direction=, offset=, price=1.6, volume=10, time='10:40:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='340', tradeid='340', direction=, offset=, price=3.0, volume=10, time='10:54:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='341', tradeid='341', direction=, offset=, price=3.2, volume=10, time='11:03:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='342', tradeid='342', direction=, offset=, price=2.0, volume=10, time='11:10:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='343', tradeid='343', direction=, offset=, price=2.8, volume=10, time='11:13:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='344', tradeid='344', direction=, offset=, price=2.6, volume=10, time='11:29:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='345', tradeid='345', direction=, offset=, price=2.6, volume=10, time='13:00:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='346', tradeid='346', direction=, offset=, price=2.8, volume=10, time='13:04:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='347', tradeid='347', direction=, offset=, price=3.2, volume=10, time='13:05:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='348', tradeid='348', direction=, offset=, price=1.8, volume=10, time='13:24:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='349', tradeid='349', direction=, offset=, price=2.6, volume=10, time='13:36:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='350', tradeid='350', direction=, offset=, price=1.8, volume=10, time='13:40:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='351', tradeid='351', direction=, offset=, price=2.4, volume=10, time='13:41:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='352', tradeid='352', direction=, offset=, price=2.2, volume=10, time='13:44:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='353', tradeid='353', direction=, offset=, price=2.4, volume=10, time='13:46:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='354', tradeid='354', direction=, offset=, price=3.6, volume=10, time='13:52:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='355', tradeid='355', direction=, offset=, price=2.6, volume=10, time='13:56:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='356', tradeid='356', direction=, offset=, price=2.8, volume=10, time='14:23:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='357', tradeid='357', direction=, offset=, price=3.0, volume=10, time='14:31:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='358', tradeid='358', direction=, offset=, price=2.8, volume=10, time='14:39:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='359', tradeid='359', direction=, offset=, price=2.6, volume=10, time='14:40:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='360', tradeid='360', direction=, offset=, price=2.4, volume=10, time='09:30:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='361', tradeid='361', direction=, offset=, price=2.4, volume=10, time='09:33:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='362', tradeid='362', direction=, offset=, price=1.2, volume=10, time='10:00:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='363', tradeid='363', direction=, offset=, price=2.6, volume=10, time='10:07:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='364', tradeid='364', direction=, offset=, price=1.8, volume=10, time='10:08:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='365', tradeid='365', direction=, offset=, price=1.8, volume=10, time='10:11:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='366', tradeid='366', direction=, offset=, price=1.8, volume=10, time='10:16:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='367', tradeid='367', direction=, offset=, price=1.8, volume=10, time='10:18:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='368', tradeid='368', direction=, offset=, price=2.2, volume=10, time='10:31:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='369', tradeid='369', direction=, offset=, price=1.2, volume=10, time='10:33:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='370', tradeid='370', direction=, offset=, price=0.8, volume=10, time='11:24:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='371', tradeid='371', direction=, offset=, price=1.2, volume=10, time='11:27:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='372', tradeid='372', direction=, offset=, price=0.6, volume=10, time='14:03:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='373', tradeid='373', direction=, offset=, price=0.6, volume=10, time='14:03:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='374', tradeid='374', direction=, offset=, price=0.2, volume=10, time='14:13:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='375', tradeid='375', direction=, offset=, price=0.2, volume=10, time='14:14:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='376', tradeid='376', direction=, offset=, price=0.4, volume=10, time='14:27:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='377', tradeid='377', direction=, offset=, price=1.4, volume=10, time='14:28:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='378', tradeid='378', direction=, offset=, price=0.8, volume=10, time='10:04:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='379', tradeid='379', direction=, offset=, price=0.8, volume=10, time='10:04:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='380', tradeid='380', direction=, offset=, price=1.6, volume=10, time='10:27:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='381', tradeid='381', direction=, offset=, price=0.8, volume=10, time='10:35:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='382', tradeid='382', direction=, offset=, price=0.2, volume=10, time='11:00:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='383', tradeid='383', direction=, offset=, price=1.0, volume=10, time='11:02:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='384', tradeid='384', direction=, offset=, price=1.2, volume=10, time='11:15:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='385', tradeid='385', direction=, offset=, price=1.2, volume=10, time='11:17:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='386', tradeid='386', direction=, offset=, price=0.4, volume=10, time='13:07:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='387', tradeid='387', direction=, offset=, price=0.4, volume=10, time='13:09:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='388', tradeid='388', direction=, offset=, price=1.2, volume=10, time='13:49:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='389', tradeid='389', direction=, offset=, price=0.6, volume=10, time='13:51:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='390', tradeid='390', direction=, offset=, price=1.6, volume=10, time='14:24:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='391', tradeid='391', direction=, offset=, price=0.6, volume=10, time='14:29:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='392', tradeid='392', direction=, offset=, price=1.8, volume=10, time='14:54:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='393', tradeid='393', direction=, offset=, price=2.8, volume=10, time='14:55:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='394', tradeid='394', direction=, offset=, price=2.6, volume=10, time='14:56:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='395', tradeid='395', direction=, offset=, price=1.6, volume=10, time='09:32:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='396', tradeid='396', direction=, offset=, price=1.2, volume=10, time='10:00:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='397', tradeid='397', direction=, offset=, price=1.4, volume=10, time='10:03:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='398', tradeid='398', direction=, offset=, price=1.6, volume=10, time='10:18:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='399', tradeid='399', direction=, offset=, price=0.4, volume=10, time='10:19:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='400', tradeid='400', direction=, offset=, price=0.2, volume=10, time='10:20:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='401', tradeid='401', direction=, offset=, price=2.2, volume=10, time='10:27:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='402', tradeid='402', direction=, offset=, price=1.6, volume=10, time='10:28:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='403', tradeid='403', direction=, offset=, price=1.0, volume=10, time='10:31:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='404', tradeid='404', direction=, offset=, price=1.4, volume=10, time='10:58:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='405', tradeid='405', direction=, offset=, price=1.0, volume=10, time='11:08:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='406', tradeid='406', direction=, offset=, price=0.8, volume=10, time='11:18:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='407', tradeid='407', direction=, offset=, price=0.4, volume=10, time='11:19:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='408', tradeid='408', direction=, offset=, price=2.0, volume=10, time='13:10:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='409', tradeid='409', direction=, offset=, price=1.2, volume=10, time='13:13:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='410', tradeid='410', direction=, offset=, price=2.4, volume=10, time='14:00:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='411', tradeid='411', direction=, offset=, price=0.8, volume=10, time='14:02:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='412', tradeid='412', direction=, offset=, price=1.4, volume=10, time='14:24:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='413', tradeid='413', direction=, offset=, price=1.4, volume=10, time='14:25:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='414', tradeid='414', direction=, offset=, price=1.0, volume=10, time='14:38:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='415', tradeid='415', direction=, offset=, price=1.0, volume=10, time='14:44:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='416', tradeid='416', direction=, offset=, price=1.6, volume=10, time='14:50:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='417', tradeid='417', direction=, offset=, price=2.2, volume=10, time='14:55:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='418', tradeid='418', direction=, offset=, price=2.2, volume=10, time='14:56:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='419', tradeid='419', direction=, offset=, price=1.0, volume=10, time='09:31:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='420', tradeid='420', direction=, offset=, price=2.2, volume=10, time='10:17:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='421', tradeid='421', direction=, offset=, price=1.0, volume=10, time='10:18:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='422', tradeid='422', direction=, offset=, price=2.4, volume=10, time='10:31:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='423', tradeid='423', direction=, offset=, price=0.6, volume=10, time='10:32:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='424', tradeid='424', direction=, offset=, price=2.0, volume=10, time='10:52:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='425', tradeid='425', direction=, offset=, price=1.0, volume=10, time='10:56:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='426', tradeid='426', direction=, offset=, price=0.8, volume=10, time='13:01:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='427', tradeid='427', direction=, offset=, price=1.4, volume=10, time='13:04:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='428', tradeid='428', direction=, offset=, price=1.8, volume=10, time='13:31:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='429', tradeid='429', direction=, offset=, price=0.8, volume=10, time='13:33:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='430', tradeid='430', direction=, offset=, price=1.4, volume=10, time='13:45:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='431', tradeid='431', direction=, offset=, price=1.0, volume=10, time='13:46:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='432', tradeid='432', direction=, offset=, price=2.2, volume=10, time='14:30:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='433', tradeid='433', direction=, offset=, price=1.8, volume=10, time='14:33:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='434', tradeid='434', direction=, offset=, price=2.0, volume=10, time='14:53:00')\n", + "TradeData(gateway_name='BACKTESTING', symbol='IF-Spread', exchange=, orderid='435', tradeid='435', direction=, offset=, price=2.0, volume=10, time='14:54:00')\n" + ] + } + ], "source": [ "for trade in engine.trades.values():\n", " print(trade)" diff --git a/vnpy/app/cta_strategy/engine.py b/vnpy/app/cta_strategy/engine.py index fbd64f04..01f16c05 100644 --- a/vnpy/app/cta_strategy/engine.py +++ b/vnpy/app/cta_strategy/engine.py @@ -628,8 +628,6 @@ class CtaEngine(BaseEngine): """ strategy = self.strategies[strategy_name] - print(datetime.now(), strategy_name, strategy.vt_symbol) - if strategy.inited: self.write_log(f"{strategy_name}已经完成初始化,禁止重复操作") return diff --git a/vnpy/app/spread_trading/backtesting.py b/vnpy/app/spread_trading/backtesting.py index b87f53c9..50d55d73 100644 --- a/vnpy/app/spread_trading/backtesting.py +++ b/vnpy/app/spread_trading/backtesting.py @@ -1,7 +1,6 @@ from collections import defaultdict from datetime import date, datetime -from typing import Callable, Type, Dict, List -from functools import lru_cache +from typing import Callable, Type import numpy as np import matplotlib.pyplot as plt @@ -10,12 +9,10 @@ from pandas import DataFrame from vnpy.trader.constant import (Direction, Offset, Exchange, Interval, Status) -from vnpy.trader.database import database_manager from vnpy.trader.object import TradeData, BarData, TickData -from vnpy.trader.utility import round_to from .template import SpreadStrategyTemplate, SpreadAlgoTemplate -from .base import SpreadData, BacktestingMode +from .base import SpreadData, BacktestingMode, load_bar_data, load_tick_data sns.set_style("whitegrid") @@ -686,73 +683,3 @@ class DailyResult: # Net pnl takes account of commission and slippage cost self.total_pnl = self.trading_pnl + self.holding_pnl self.net_pnl = self.total_pnl - self.commission - self.slippage - - -@lru_cache(maxsize=999) -def load_bar_data( - spread: SpreadData, - interval: Interval, - start: datetime, - end: datetime, - pricetick: float -): - """""" - # Load bar data of each spread leg - leg_bars: Dict[str, Dict] = {} - - for vt_symbol in spread.legs.keys(): - symbol, exchange_str = vt_symbol.split(".") - exchange = Exchange(exchange_str) - - bar_data: List[BarData] = database_manager.load_bar_data( - symbol, exchange, interval, start, end - ) - - bars: Dict[datetime, BarData] = {bar.datetime: bar for bar in bar_data} - leg_bars[vt_symbol] = bars - - # Calculate spread bar data - spread_bars: List[BarData] = [] - - for dt in bars.keys(): - spread_price = 0 - spread_available = True - - for leg in spread.legs.values(): - leg_bar = leg_bars[leg.vt_symbol].get(dt, None) - - if leg_bar: - price_multiplier = spread.price_multipliers[leg.vt_symbol] - spread_price += price_multiplier * leg_bar.close_price - else: - spread_available = False - - if spread_available: - spread_price = round_to(spread_price, pricetick) - - spread_bar = BarData( - symbol=spread.name, - exchange=exchange.LOCAL, - datetime=dt, - interval=interval, - open_price=spread_price, - high_price=spread_price, - low_price=spread_price, - close_price=spread_price, - gateway_name=BacktestingEngine.gateway_name, - ) - spread_bars.append(spread_bar) - - return spread_bars - - -@lru_cache(maxsize=999) -def load_tick_data( - spread: SpreadData, - start: datetime, - end: datetime -): - """""" - return database_manager.load_tick_data( - spread.name, Exchange.LOCAL, start, end - ) diff --git a/vnpy/app/spread_trading/base.py b/vnpy/app/spread_trading/base.py index cace98f3..1b84f89d 100644 --- a/vnpy/app/spread_trading/base.py +++ b/vnpy/app/spread_trading/base.py @@ -1,10 +1,14 @@ from typing import Dict, List from datetime import datetime from enum import Enum +from functools import lru_cache -from vnpy.trader.object import TickData, PositionData, TradeData, ContractData -from vnpy.trader.constant import Direction, Offset, Exchange -from vnpy.trader.utility import floor_to, ceil_to +from vnpy.trader.object import ( + TickData, PositionData, TradeData, ContractData, BarData +) +from vnpy.trader.constant import Direction, Offset, Exchange, Interval +from vnpy.trader.utility import floor_to, ceil_to, round_to, extract_vt_symbol +from vnpy.trader.database import database_manager EVENT_SPREAD_DATA = "eSpreadData" @@ -353,3 +357,73 @@ def calculate_inverse_volume( class BacktestingMode(Enum): BAR = 1 TICK = 2 + + +@lru_cache(maxsize=999) +def load_bar_data( + spread: SpreadData, + interval: Interval, + start: datetime, + end: datetime, + pricetick: float = 0 +): + """""" + # Load bar data of each spread leg + leg_bars: Dict[str, Dict] = {} + + for vt_symbol in spread.legs.keys(): + symbol, exchange = extract_vt_symbol(vt_symbol) + + bar_data: List[BarData] = database_manager.load_bar_data( + symbol, exchange, interval, start, end + ) + + bars: Dict[datetime, BarData] = {bar.datetime: bar for bar in bar_data} + leg_bars[vt_symbol] = bars + + # Calculate spread bar data + spread_bars: List[BarData] = [] + + for dt in bars.keys(): + spread_price = 0 + spread_available = True + + for leg in spread.legs.values(): + leg_bar = leg_bars[leg.vt_symbol].get(dt, None) + + if leg_bar: + price_multiplier = spread.price_multipliers[leg.vt_symbol] + spread_price += price_multiplier * leg_bar.close_price + else: + spread_available = False + + if spread_available: + if pricetick: + spread_price = round_to(spread_price, pricetick) + + spread_bar = BarData( + symbol=spread.name, + exchange=exchange.LOCAL, + datetime=dt, + interval=interval, + open_price=spread_price, + high_price=spread_price, + low_price=spread_price, + close_price=spread_price, + gateway_name="SPREAD", + ) + spread_bars.append(spread_bar) + + return spread_bars + + +@lru_cache(maxsize=999) +def load_tick_data( + spread: SpreadData, + start: datetime, + end: datetime +): + """""" + return database_manager.load_tick_data( + spread.name, Exchange.LOCAL, start, end + ) diff --git a/vnpy/app/spread_trading/engine.py b/vnpy/app/spread_trading/engine.py index 8550e0de..65902642 100644 --- a/vnpy/app/spread_trading/engine.py +++ b/vnpy/app/spread_trading/engine.py @@ -5,6 +5,7 @@ from typing import List, Dict, Set, Callable, Any, Type from collections import defaultdict from copy import copy from pathlib import Path +from datetime import datetime, timedelta from vnpy.event import EventEngine, Event from vnpy.trader.engine import BaseEngine, MainEngine @@ -17,14 +18,17 @@ from vnpy.trader.object import ( TickData, ContractData, LogData, SubscribeRequest, OrderRequest ) -from vnpy.trader.constant import Direction, Offset, OrderType +from vnpy.trader.constant import ( + Direction, Offset, OrderType, Interval +) from vnpy.trader.converter import OffsetConverter from .base import ( LegData, SpreadData, EVENT_SPREAD_DATA, EVENT_SPREAD_POS, EVENT_SPREAD_ALGO, EVENT_SPREAD_LOG, - EVENT_SPREAD_STRATEGY + EVENT_SPREAD_STRATEGY, + load_bar_data, load_tick_data ) from .template import SpreadAlgoTemplate, SpreadStrategyTemplate from .algo import SpreadTakerAlgo @@ -1024,3 +1028,25 @@ class SpreadStrategyEngine: subject = "价差策略引擎" self.main_engine.send_email(subject, msg) + + def load_bar( + self, spread: SpreadData, days: int, interval: Interval, callback: Callable + ): + """""" + end = datetime.now() + start = end - timedelta(days) + + bars = load_bar_data(spread, interval, start, end) + + for bar in bars: + callback(bar) + + def load_tick(self, spread: SpreadData, days: int, callback: Callable): + """""" + end = datetime.now() + start = end - timedelta(days) + + ticks = load_tick_data(spread, start, end) + + for tick in ticks: + callback(tick)