[bug fix]
This commit is contained in:
parent
d9a06c11cd
commit
f9aad85db9
55
Q_n_A.md
55
Q_n_A.md
@ -2,59 +2,67 @@
|
|||||||
--------------------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------------------
|
||||||
###FAQ:
|
###FAQ:
|
||||||
|
|
||||||
#3、碰到的问题:找不到vnpy.xx.xx(原2.7环境)
|
3、碰到的问题:找不到vnpy.xx.xx(原2.7环境)
|
||||||
|
|
||||||
可能你使用了vnpy的原版,安装到conda环境中了。需要先卸载 pip uninstall vnpy
|
可能你使用了vnpy的原版,安装到conda环境中了。需要先卸载 pip uninstall vnpy
|
||||||
|
|
||||||
|
|
||||||
#4、碰到的问题:importError: libGL.so.1: cannot open shared object file: No such file or directory
|
4、碰到的问题:importError: libGL.so.1: cannot open shared object file: No such file or directory
|
||||||
|
|
||||||
ubuntu下:sudo apt install libgl1-mesa-glx
|
ubuntu下:sudo apt install libgl1-mesa-glx
|
||||||
centOS下:sudo yum install mesa-libGL.x86_64
|
centOS下:sudo yum install mesa-libGL.x86_64
|
||||||
|
|
||||||
#5、碰到的问题:version `GLIBCXX_3.4.21' not found
|
5、碰到的问题:version `GLIBCXX_3.4.21' not found
|
||||||
|
|
||||||
conda install libgcc
|
conda install libgcc
|
||||||
若出现更高版本需求,参见第10点
|
若出现更高版本需求,参见第10点
|
||||||
|
|
||||||
#6、碰到的问题:在3.7 env下安装RqPlus时,报错:talib/common.c:242:28: fatal error: ta-lib/ta_defs.h: No such file or directory
|
6、碰到的问题:在3.7 env下安装RqPlus时,报错:talib/common.c:242:28: fatal error: ta-lib/ta_defs.h: No such file or directory
|
||||||
|
|
||||||
locate ta_defs.h
|
locate ta_defs.h
|
||||||
找到地址:/home/user/anaconda3/pkgs/ta-lib-0.4.9-np111py27_0/include/ta-lib
|
找到地址:/home/user/anaconda3/pkgs/ta-lib-0.4.9-np111py27_0/include/ta-lib
|
||||||
# 复制一份到/usr/include目录下
|
# 复制一份到/usr/include目录下
|
||||||
sudo cp /home/user/anaconda3/pkgs/ta-lib-0.4.9-np111py27_0/include/ta-lib /usr/include -R
|
sudo cp /home/user/anaconda3/pkgs/ta-lib-0.4.9-np111py27_0/include/ta-lib /usr/include -R
|
||||||
|
|
||||||
#7、碰到的问题:安装某些组件,提示 cl.exe Not found
|
7、碰到的问题:安装某些组件,提示 cl.exe Not found
|
||||||
|
|
||||||
如果你安装了VC基础组件,需要增加一个用户环境变量,把"C:\Program Files (x86)\Microsoft Visual Studio\Shared\14.0\VC\bin" 添加到path变量中
|
如果你安装了VC基础组件,需要增加一个用户环境变量,把"C:\Program Files (x86)\Microsoft Visual Studio\Shared\14.0\VC\bin" 添加到path变量中
|
||||||
|
|
||||||
#8、Install Ta-Lib
|
8、Install Ta-Lib
|
||||||
如果你用py37虚拟环境
|
|
||||||
source activate py37
|
|
||||||
|
|
||||||
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
|
如果你用py37虚拟环境
|
||||||
conda config --set show_channel_urls yes
|
source activate py37
|
||||||
conda install -c quantopian ta-lib=0.4.9
|
|
||||||
|
|
||||||
# 9、数字货币的增量安装
|
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
|
||||||
conda install scipy
|
conda config --set show_channel_urls yes
|
||||||
|
conda install -c quantopian ta-lib=0.4.9
|
||||||
|
|
||||||
pip install autobahn
|
9、数字货币的增量安装
|
||||||
pip install twisted
|
|
||||||
若出现找不到rc.exe, 请先使用vs x86&x64界面,激活py37后,再运行
|
conda install scipy
|
||||||
pip install pyOpenSSL
|
|
||||||
|
pip install autobahn
|
||||||
|
pip install twisted
|
||||||
|
若出现找不到rc.exe, 请先使用vs x86&x64界面,激活py37后,再运行
|
||||||
|
pip install pyOpenSSL
|
||||||
|
|
||||||
|
10、升级gcc
|
||||||
|
|
||||||
# 10、升级gcc
|
|
||||||
使用奇正MOM的CTP API时,提示`GLIBCXX_3.4.22' not found,当前centos最高版本是 3.4.21,通过yum不能升级,需要手工下载升级。
|
使用奇正MOM的CTP API时,提示`GLIBCXX_3.4.22' not found,当前centos最高版本是 3.4.21,通过yum不能升级,需要手工下载升级。
|
||||||
|
|
||||||
wget http://ftp.de.debian.org/debian/pool/main/g/gcc-9/libstdc++6_9.2.1-8_amd64.deb
|
wget http://ftp.de.debian.org/debian/pool/main/g/gcc-9/libstdc++6_9.2.1-8_amd64.deb
|
||||||
解压
|
解压
|
||||||
|
|
||||||
ar -x libstdc++6_9.2.1-8_amd64.deb
|
ar -x libstdc++6_9.2.1-8_amd64.deb
|
||||||
(就是 ar 命令,不是tar)
|
(就是 ar 命令,不是tar)
|
||||||
tar -xvf data.tar.xz
|
tar -xvf data.tar.xz
|
||||||
|
|
||||||
安装
|
安装
|
||||||
|
|
||||||
删除: rm /usr/lib64/libstdc++.so.6
|
删除: rm /usr/lib64/libstdc++.so.6
|
||||||
拷贝: cp usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28 /usr/lib64/
|
拷贝: cp usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.28 /usr/lib64/
|
||||||
连接: ln -s /usr/lib64/libstdc++.so.6.0.28 /usr/lib64/libstdc++.so.6
|
连接: ln -s /usr/lib64/libstdc++.so.6.0.28 /usr/lib64/libstdc++.so.6
|
||||||
结果
|
|
||||||
|
结果
|
||||||
|
|
||||||
strings /usr/lib64/libstdc++.so.6 | grep GLIBCXX
|
strings /usr/lib64/libstdc++.so.6 | grep GLIBCXX
|
||||||
|
|
||||||
@ -86,7 +94,8 @@ pip install pyOpenSSL
|
|||||||
GLIBCXX_3.4.25
|
GLIBCXX_3.4.25
|
||||||
GLIBCXX_DEBUG_MESSAGE_LENGTH
|
GLIBCXX_DEBUG_MESSAGE_LENGTH
|
||||||
|
|
||||||
# 11、升级glibc
|
11、升级glibc
|
||||||
|
|
||||||
使用奇正MOM的CTP API时,提示`GLIBC_2.18' not found,当前centos最高版本是 3.4.21,通过yum不能升级,需要手工下载升级。
|
使用奇正MOM的CTP API时,提示`GLIBC_2.18' not found,当前centos最高版本是 3.4.21,通过yum不能升级,需要手工下载升级。
|
||||||
|
|
||||||
root 用户登录
|
root 用户登录
|
||||||
|
16
examples/vn_trader/ar_setting.json
Normal file
16
examples/vn_trader/ar_setting.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"mongo_db":
|
||||||
|
{
|
||||||
|
"host": "192.168.0.207",
|
||||||
|
"port": 27017
|
||||||
|
},
|
||||||
|
"accounts":
|
||||||
|
{
|
||||||
|
|
||||||
|
"ctp":
|
||||||
|
{
|
||||||
|
"copy_history_trades": true,
|
||||||
|
"copy_history_orders": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
examples/vn_trader/cta_strategy_pro_config.json
Normal file
4
examples/vn_trader/cta_strategy_pro_config.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"accountid" : "112022",
|
||||||
|
"strategy_group": "win01"
|
||||||
|
}
|
@ -5,10 +5,10 @@ from vnpy.trader.engine import MainEngine
|
|||||||
from vnpy.trader.ui import MainWindow, create_qapp
|
from vnpy.trader.ui import MainWindow, create_qapp
|
||||||
|
|
||||||
# from vnpy.gateway.binance import BinanceGateway
|
# from vnpy.gateway.binance import BinanceGateway
|
||||||
from vnpy.gateway.bitmex import BitmexGateway
|
#from vnpy.gateway.bitmex import BitmexGateway
|
||||||
# from vnpy.gateway.futu import FutuGateway
|
# from vnpy.gateway.futu import FutuGateway
|
||||||
# from vnpy.gateway.ib import IbGateway
|
# from vnpy.gateway.ib import IbGateway
|
||||||
# from vnpy.gateway.ctp import CtpGateway
|
from vnpy.gateway.ctp import CtpGateway
|
||||||
# from vnpy.gateway.ctptest import CtptestGateway
|
# from vnpy.gateway.ctptest import CtptestGateway
|
||||||
# from vnpy.gateway.mini import MiniGateway
|
# from vnpy.gateway.mini import MiniGateway
|
||||||
# from vnpy.gateway.sopt import SoptGateway
|
# from vnpy.gateway.sopt import SoptGateway
|
||||||
@ -31,30 +31,33 @@ from vnpy.gateway.bitmex import BitmexGateway
|
|||||||
# from vnpy.gateway.coinbase import CoinbaseGateway
|
# from vnpy.gateway.coinbase import CoinbaseGateway
|
||||||
# from vnpy.gateway.bitstamp import BitstampGateway
|
# from vnpy.gateway.bitstamp import BitstampGateway
|
||||||
# from vnpy.gateway.gateios import GateiosGateway
|
# from vnpy.gateway.gateios import GateiosGateway
|
||||||
from vnpy.gateway.bybit import BybitGateway
|
# from vnpy.gateway.bybit import BybitGateway
|
||||||
|
|
||||||
# from vnpy.app.cta_strategy import CtaStrategyApp
|
from vnpy.app.cta_strategy_pro import CtaStrategyProApp
|
||||||
# from vnpy.app.csv_loader import CsvLoaderApp
|
# from vnpy.app.csv_loader import CsvLoaderApp
|
||||||
# from vnpy.app.algo_trading import AlgoTradingApp
|
# from vnpy.app.algo_trading import AlgoTradingApp
|
||||||
from vnpy.app.cta_backtester import CtaBacktesterApp
|
from vnpy.app.cta_backtester import CtaBacktesterApp
|
||||||
# from vnpy.app.data_recorder import DataRecorderApp
|
# from vnpy.app.data_recorder import DataRecorderApp
|
||||||
# from vnpy.app.risk_manager import RiskManagerApp
|
from vnpy.app.tick_recorder import TickRecorderApp
|
||||||
|
from vnpy.app.risk_manager import RiskManagerApp
|
||||||
# from vnpy.app.script_trader import ScriptTraderApp
|
# from vnpy.app.script_trader import ScriptTraderApp
|
||||||
# from vnpy.app.rpc_service import RpcServiceApp
|
# from vnpy.app.rpc_service import RpcServiceApp
|
||||||
# from vnpy.app.spread_trading import SpreadTradingApp
|
# from vnpy.app.spread_trading import SpreadTradingApp
|
||||||
from vnpy.app.portfolio_manager import PortfolioManagerApp
|
# from vnpy.app.portfolio_manager import PortfolioManagerApp
|
||||||
|
from vnpy.app.account_recorder import AccountRecorderApp
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
""""""
|
""""""
|
||||||
qapp = create_qapp()
|
qapp = create_qapp()
|
||||||
|
|
||||||
event_engine = EventEngine()
|
event_engine = EventEngine(debug=True, over_ms=200)
|
||||||
|
|
||||||
main_engine = MainEngine(event_engine)
|
main_engine = MainEngine(event_engine)
|
||||||
|
|
||||||
# main_engine.add_gateway(BinanceGateway)
|
# main_engine.add_gateway(BinanceGateway)
|
||||||
# main_engine.add_gateway(CtpGateway)
|
main_engine.add_gateway(CtpGateway, gateway_name='ctp')
|
||||||
|
main_engine.add_gateway(CtpGateway, gateway_name='ctp_yh01')
|
||||||
|
|
||||||
# main_engine.add_gateway(CtptestGateway)
|
# main_engine.add_gateway(CtptestGateway)
|
||||||
# main_engine.add_gateway(MiniGateway)
|
# main_engine.add_gateway(MiniGateway)
|
||||||
# main_engine.add_gateway(SoptGateway)
|
# main_engine.add_gateway(SoptGateway)
|
||||||
@ -62,7 +65,7 @@ def main():
|
|||||||
# main_engine.add_gateway(FemasGateway)
|
# main_engine.add_gateway(FemasGateway)
|
||||||
# main_engine.add_gateway(IbGateway)
|
# main_engine.add_gateway(IbGateway)
|
||||||
# main_engine.add_gateway(FutuGateway)
|
# main_engine.add_gateway(FutuGateway)
|
||||||
main_engine.add_gateway(BitmexGateway)
|
#main_engine.add_gateway(BitmexGateway)
|
||||||
# main_engine.add_gateway(TigerGateway)
|
# main_engine.add_gateway(TigerGateway)
|
||||||
# main_engine.add_gateway(OesGateway)
|
# main_engine.add_gateway(OesGateway)
|
||||||
# main_engine.add_gateway(OkexGateway)
|
# main_engine.add_gateway(OkexGateway)
|
||||||
@ -80,18 +83,21 @@ def main():
|
|||||||
# main_engine.add_gateway(CoinbaseGateway)
|
# main_engine.add_gateway(CoinbaseGateway)
|
||||||
# main_engine.add_gateway(BitstampGateway)
|
# main_engine.add_gateway(BitstampGateway)
|
||||||
# main_engine.add_gateway(GateiosGateway)
|
# main_engine.add_gateway(GateiosGateway)
|
||||||
main_engine.add_gateway(BybitGateway)
|
#main_engine.add_gateway(BybitGateway)
|
||||||
|
|
||||||
# main_engine.add_app(CtaStrategyApp)
|
#main_engine.add_app(CtaStrategyApp)
|
||||||
main_engine.add_app(CtaBacktesterApp)
|
main_engine.add_app(CtaStrategyProApp)
|
||||||
|
#main_engine.add_app(CtaBacktesterApp)
|
||||||
# main_engine.add_app(CsvLoaderApp)
|
# main_engine.add_app(CsvLoaderApp)
|
||||||
# main_engine.add_app(AlgoTradingApp)
|
# main_engine.add_app(AlgoTradingApp)
|
||||||
# main_engine.add_app(DataRecorderApp)
|
# main_engine.add_app(DataRecorderApp)
|
||||||
# main_engine.add_app(RiskManagerApp)
|
# main_engine.add_app(TickRecorderApp)
|
||||||
|
main_engine.add_app(RiskManagerApp)
|
||||||
# main_engine.add_app(ScriptTraderApp)
|
# main_engine.add_app(ScriptTraderApp)
|
||||||
# main_engine.add_app(RpcServiceApp)
|
# main_engine.add_app(RpcServiceApp)
|
||||||
# main_engine.add_app(SpreadTradingApp)
|
# main_engine.add_app(SpreadTradingApp)
|
||||||
main_engine.add_app(PortfolioManagerApp)
|
# main_engine.add_app(PortfolioManagerApp)
|
||||||
|
# main_engine.add_app(AccountRecorderApp)
|
||||||
|
|
||||||
main_window = MainWindow(main_engine, event_engine)
|
main_window = MainWindow(main_engine, event_engine)
|
||||||
main_window.showMaximized()
|
main_window.showMaximized()
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from vnpy.trader.app import BaseApp
|
from vnpy.trader.app import BaseApp
|
||||||
from vnpy.trader.constant import Direction,Offset
|
from vnpy.trader.constant import Direction,Offset,Status
|
||||||
from vnpy.trader.object import TickData, BarData, TradeData, OrderData
|
from vnpy.trader.object import TickData, BarData, TradeData, OrderData
|
||||||
from vnpy.trader.utility import BarGenerator, ArrayManager
|
from vnpy.trader.utility import BarGenerator, ArrayManager
|
||||||
|
from .cta_position import CtaPosition
|
||||||
|
from .cta_line_bar import CtaLineBar, CtaMinuteBar, CtaHourBar, CtaDayBar, CtaWeekBar
|
||||||
|
from .cta_policy import CtaPolicy
|
||||||
|
from .cta_grid_trade import CtaGrid, CtaGridTrade
|
||||||
from .base import APP_NAME, StopOrder
|
from .base import APP_NAME, StopOrder
|
||||||
from .engine import CtaEngine
|
from .engine import CtaEngine
|
||||||
|
|
||||||
from .template import CtaTemplate, CtaSignal, TargetPosTemplate
|
from .template import CtaTemplate, CtaSignal, TargetPosTemplate, CtaProTemplate
|
||||||
|
|
||||||
class CtaStrategyProApp(BaseApp):
|
class CtaStrategyProApp(BaseApp):
|
||||||
""""""
|
""""""
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
"""
|
"""
|
||||||
Defines constants and objects used in CtaStrategyPro App.
|
Defines constants and objects used in CtaStrategyPro App.
|
||||||
"""
|
"""
|
||||||
|
from abc import ABC
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
from logging import INFO, ERROR
|
||||||
from vnpy.trader.constant import Direction, Offset, Interval
|
from vnpy.trader.constant import Direction, Offset, Interval
|
||||||
|
|
||||||
APP_NAME = "CtaStrategyPro"
|
APP_NAME = "CtaStrategyPro"
|
||||||
@ -92,3 +92,30 @@ INTERVAL_DELTA_MAP = {
|
|||||||
Interval.HOUR: timedelta(hours=1),
|
Interval.HOUR: timedelta(hours=1),
|
||||||
Interval.DAILY: timedelta(days=1),
|
Interval.DAILY: timedelta(days=1),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CtaComponent(ABC):
|
||||||
|
""" CTA策略基础组件"""
|
||||||
|
|
||||||
|
def __init__(self, strategy=None, **kwargs):
|
||||||
|
"""
|
||||||
|
构造
|
||||||
|
:param strategy:
|
||||||
|
"""
|
||||||
|
self.strategy = strategy
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
def write_log(self, content: str):
|
||||||
|
"""记录日志"""
|
||||||
|
if self.strategy:
|
||||||
|
self.strategy.write_log(msg=content, level=INFO)
|
||||||
|
else:
|
||||||
|
print(content)
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
def write_error(self, content: str, level: int = ERROR):
|
||||||
|
"""记录错误日志"""
|
||||||
|
if self.strategy:
|
||||||
|
self.strategy.write_log(msg=content, level=level)
|
||||||
|
else:
|
||||||
|
print(content, file=sys.stderr)
|
||||||
|
|
||||||
|
@ -10,10 +10,9 @@ import traceback
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
from typing import List
|
||||||
from vnpy.trader.utility import get_folder_path
|
from vnpy.trader.utility import get_folder_path
|
||||||
from vnpy.app.cta_strategy_pro.base import Direction
|
from vnpy.app.cta_strategy_pro.base import Direction, CtaComponent
|
||||||
from vnpy.app.cta_strategy_pro.template import CtaComponent
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
网格交易,用于套利单
|
网格交易,用于套利单
|
||||||
@ -35,37 +34,47 @@ TREND_GRID = 'trend' # 趋势网格
|
|||||||
LOCK_GRID = 'lock' # 对锁网格
|
LOCK_GRID = 'lock' # 对锁网格
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class CtaGrid(object):
|
class CtaGrid(object):
|
||||||
"""网格类
|
"""网格类
|
||||||
它是网格交易的最小单元
|
它是网格交易的最小单元
|
||||||
包括交易方向,开仓价格,平仓价格,止损价格,开仓状态,平仓状态
|
包括交易方向,开仓价格,平仓价格,止损价格,开仓状态,平仓状态
|
||||||
"""
|
"""
|
||||||
id: str = str(uuid.uuid1()) # gid
|
|
||||||
|
|
||||||
direction: Direction = Direction.NET # 交易方向(LONG:多,正套;SHORT:空,反套)
|
def __init__(self,
|
||||||
open_price: float = 0 # 开仓价格
|
direction: Direction = None,
|
||||||
close_price: float = 0 # 止盈价格
|
open_price: float = 0,
|
||||||
stop_price: float = 0 # 止损价格
|
close_price: float = 0,
|
||||||
|
stop_price: float = 0,
|
||||||
|
vt_symbol: str = '',
|
||||||
|
volume: float = 0,
|
||||||
|
traded_volume: float = 0,
|
||||||
|
order_status: bool = False,
|
||||||
|
open_status: bool = False,
|
||||||
|
close_status: bool = False,
|
||||||
|
open_time: datetime = None,
|
||||||
|
order_time: datetime = None,
|
||||||
|
reuse_count: int = 0,
|
||||||
|
type: str = ''
|
||||||
|
):
|
||||||
|
|
||||||
vt_symbol: str = '' # 品种合约
|
self.id: str = str(uuid.uuid1()) # gid
|
||||||
volume: float = 0 # 开仓数量( 兼容数字货币 )
|
self.direction = direction # 交易方向(LONG:多,正套;SHORT:空,反套)
|
||||||
|
self.open_price = open_price # 开仓价格
|
||||||
traded_volume: float = 0 # 已成交数量 开仓时,为开仓数量,平仓时,为平仓数量
|
self.close_price = close_price # 止盈价格
|
||||||
|
self.stop_price = stop_price # 止损价格
|
||||||
order_status: bool = False # 挂单状态: True,已挂单,False,未挂单
|
self.vt_symbol = vt_symbol # 品种合约
|
||||||
order_ids: list[str] = field(default_factory=list) # order_id list
|
self.volume = volume # 开仓数量( 兼容数字货币 )
|
||||||
open_status: bool = False # 开仓状态
|
self.traded_volume = traded_volume # 已成交数量 开仓时,为开仓数量,平仓时,为平仓数量
|
||||||
close_status: bool = False # 平仓状态
|
self.order_status = order_status # 挂单状态: True,已挂单,False,未挂单
|
||||||
|
self.order_ids = [] # order_id list
|
||||||
open_time: datetime = None # 开仓时间
|
self.open_status = open_status # 开仓状态
|
||||||
order_time: datetime = None # 委托时间
|
self.close_status = close_status # 平仓状态
|
||||||
|
self.open_time = open_time # 开仓时间
|
||||||
lock_grid_ids: list[str] = field(default_factory=list) # 锁单的网格,[gid,gid]
|
self.order_time = order_time # 委托时间
|
||||||
reuse_count: int = 0 # 重用次数(0, 平仓后是否删除)
|
self.lock_grid_ids = [] # 锁单的网格,[gid,gid]
|
||||||
type: str = '' # 网格类型标签
|
self.reuse_count = reuse_count # 重用次数(0, 平仓后是否删除)
|
||||||
|
self.type = type # 网格类型标签
|
||||||
snapshot: dict = field(default_factory=dict) # 切片数据,如记录开仓点时的某些状态数据
|
self.snapshot = {} # 切片数据,如记录开仓点时的某些状态数据
|
||||||
|
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
"""输出JSON"""
|
"""输出JSON"""
|
||||||
@ -156,10 +165,10 @@ class CtaGridTrade(CtaComponent):
|
|||||||
vol,网格开仓数
|
vol,网格开仓数
|
||||||
minDiff, 最小价格跳动
|
minDiff, 最小价格跳动
|
||||||
"""
|
"""
|
||||||
super(CtaGridTrade).__init__(strategy=strategy)
|
super(CtaGridTrade, self).__init__(strategy=strategy)
|
||||||
|
|
||||||
self.price_tick = kwargs.get('price_tick', 1)
|
self.price_tick = kwargs.get('price_tick', 1)
|
||||||
self.jsonName = self.strategy.name # 策略名称
|
self.json_name = self.strategy.strategy_name # 策略名称
|
||||||
self.max_lots = kwargs.get('max_lots', 10) # 缺省网格数量
|
self.max_lots = kwargs.get('max_lots', 10) # 缺省网格数量
|
||||||
self.grid_height = kwargs.get('grid_height', 10 * self.price_tick) # 最小网格高度
|
self.grid_height = kwargs.get('grid_height', 10 * self.price_tick) # 最小网格高度
|
||||||
self.grid_win = kwargs.get('grid_win', 10 * self.price_tick) # 最小止盈高度
|
self.grid_win = kwargs.get('grid_win', 10 * self.price_tick) # 最小止盈高度
|
||||||
@ -176,7 +185,8 @@ class CtaGridTrade(CtaComponent):
|
|||||||
self.max_up_open_price = 0.0 # 上网格最高开仓价
|
self.max_up_open_price = 0.0 # 上网格最高开仓价
|
||||||
self.min_dn_open_price = 0.0 # 下网格最小开仓价
|
self.min_dn_open_price = 0.0 # 下网格最小开仓价
|
||||||
|
|
||||||
self.json_file_path = os.path.join(get_folder_path('data'), f'{self.jsonName}_Grids.json') # 网格的路径
|
# 网格json文件的路径
|
||||||
|
self.json_file_path = os.path.join(get_folder_path('data'), f'{self.json_name}_Grids.json')
|
||||||
|
|
||||||
def get_volume_rate(self, idx: int = 0):
|
def get_volume_rate(self, idx: int = 0):
|
||||||
"""获取网格索引对应的开仓数量比例"""
|
"""获取网格索引对应的开仓数量比例"""
|
||||||
@ -554,7 +564,7 @@ class CtaGridTrade(CtaComponent):
|
|||||||
|
|
||||||
if direction == Direction.SHORT:
|
if direction == Direction.SHORT:
|
||||||
for x in self.up_grids[:]:
|
for x in self.up_grids[:]:
|
||||||
if x.id in id:
|
if x.id in ids:
|
||||||
self.write_log(u'清除上网格[open={},close={},stop={},volume={}]'
|
self.write_log(u'清除上网格[open={},close={},stop={},volume={}]'
|
||||||
.format(x.open_price, x.close_price, x.stop_price, x.volume))
|
.format(x.open_price, x.close_price, x.stop_price, x.volume))
|
||||||
self.up_grids.remove(x)
|
self.up_grids.remove(x)
|
||||||
@ -873,12 +883,12 @@ class CtaGridTrade(CtaComponent):
|
|||||||
grids_save_path = get_folder_path('data')
|
grids_save_path = get_folder_path('data')
|
||||||
|
|
||||||
# 确保json名字与策略一致
|
# 确保json名字与策略一致
|
||||||
if self.jsonName != self.strategy.name:
|
if self.json_name != self.strategy.strategy_name:
|
||||||
self.write_log(u'JsonName {} 与 上层策略名{} 不一致.'.format(self.jsonName, self.strategy.name))
|
self.write_log(u'JsonName {} 与 上层策略名{} 不一致.'.format(self.json_name, self.strategy.strategy_name))
|
||||||
self.jsonName = self.strategy.name
|
self.json_name = self.strategy.strategy_name
|
||||||
|
|
||||||
# 新版网格持久化文件
|
# 新版网格持久化文件
|
||||||
grid_json_file = os.path.join(grids_save_path, u'{}_Grids.json'.format(self.jsonName))
|
grid_json_file = os.path.join(grids_save_path, u'{}_Grids.json'.format(self.json_name))
|
||||||
self.json_file_path = grid_json_file
|
self.json_file_path = grid_json_file
|
||||||
|
|
||||||
data = {}
|
data = {}
|
||||||
@ -907,12 +917,12 @@ class CtaGridTrade(CtaComponent):
|
|||||||
data = {}
|
data = {}
|
||||||
grids_save_path = get_folder_path('data')
|
grids_save_path = get_folder_path('data')
|
||||||
|
|
||||||
if self.jsonName != self.strategy.name:
|
if self.json_name != self.strategy.strategy_name:
|
||||||
self.write_log(u'JsonName {} 与 上层策略名{} 不一致.'.format(self.jsonName, self.strategy.name))
|
self.write_log(u'JsonName {} 与 上层策略名{} 不一致.'.format(self.json_name, self.strategy.strategy_name))
|
||||||
self.jsonName = self.strategy.name
|
self.json_name = self.strategy.strategy_name
|
||||||
|
|
||||||
# 若json文件不存在,就保存一个;若存在,就优先使用数据文件
|
# 若json文件不存在,就保存一个;若存在,就优先使用数据文件
|
||||||
grid_json_file = os.path.join(grids_save_path, u'{}_Grids.json'.format(self.jsonName))
|
grid_json_file = os.path.join(grids_save_path, u'{}_Grids.json'.format(self.json_name))
|
||||||
if not os.path.exists(grid_json_file):
|
if not os.path.exists(grid_json_file):
|
||||||
data['up_grids'] = []
|
data['up_grids'] = []
|
||||||
data['dn_grids'] = []
|
data['dn_grids'] = []
|
||||||
@ -970,7 +980,7 @@ class CtaGridTrade(CtaComponent):
|
|||||||
|
|
||||||
data_folder = get_folder_path('data')
|
data_folder = get_folder_path('data')
|
||||||
|
|
||||||
self.jsonName = new_name
|
self.json_name = new_name
|
||||||
# 旧文件
|
# 旧文件
|
||||||
old_json_file = os.path.join(data_folder, u'{0}_Grids.json'.format(old_name))
|
old_json_file = os.path.join(data_folder, u'{0}_Grids.json'.format(old_name))
|
||||||
|
|
||||||
|
@ -1016,7 +1016,12 @@ class CtaLineBar(object):
|
|||||||
def first_tick(self, tick: TickData):
|
def first_tick(self, tick: TickData):
|
||||||
""" K线的第一个Tick数据"""
|
""" K线的第一个Tick数据"""
|
||||||
|
|
||||||
self.cur_bar = BarData() # 创建新的K线
|
self.cur_bar = BarData(
|
||||||
|
gateway_name=tick.gateway_name,
|
||||||
|
symbol=tick.symbol,
|
||||||
|
exchange=tick.exchange,
|
||||||
|
datetime=tick.datetime
|
||||||
|
) # 创建新的K线
|
||||||
# 计算K线的整点分钟周期,这里周期最小是1分钟。如果你是采用非整点分钟,例如1.5分钟,请把这段注解掉
|
# 计算K线的整点分钟周期,这里周期最小是1分钟。如果你是采用非整点分钟,例如1.5分钟,请把这段注解掉
|
||||||
if self.minute_interval and self.interval == Interval.SECOND:
|
if self.minute_interval and self.interval == Interval.SECOND:
|
||||||
self.minute_interval = int(self.bar_interval / 60)
|
self.minute_interval = int(self.bar_interval / 60)
|
||||||
@ -1506,7 +1511,7 @@ class CtaLineBar(object):
|
|||||||
if self.para_ma1_len > 0:
|
if self.para_ma1_len > 0:
|
||||||
count_len = min(self.bar_len, self.para_ma1_len)
|
count_len = min(self.bar_len, self.para_ma1_len)
|
||||||
if count_len > 0:
|
if count_len > 0:
|
||||||
close_ma_array = ta.MA(np.append(self.close_array[-count_len:], [self.line_bar[-1].close]), count_len)
|
close_ma_array = ta.MA(np.append(self.close_array[-count_len:], [self.line_bar[-1].close_price]), count_len)
|
||||||
self._rt_ma1 = round(float(close_ma_array[-1]), self.round_n)
|
self._rt_ma1 = round(float(close_ma_array[-1]), self.round_n)
|
||||||
|
|
||||||
# 计算斜率
|
# 计算斜率
|
||||||
@ -1517,7 +1522,7 @@ class CtaLineBar(object):
|
|||||||
if self.para_ma2_len > 0:
|
if self.para_ma2_len > 0:
|
||||||
count_len = min(self.bar_len, self.para_ma2_len)
|
count_len = min(self.bar_len, self.para_ma2_len)
|
||||||
if count_len > 0:
|
if count_len > 0:
|
||||||
close_ma_array = ta.MA(np.append(self.close_array[-count_len:], [self.line_bar[-1].close]), count_len)
|
close_ma_array = ta.MA(np.append(self.close_array[-count_len:], [self.line_bar[-1].close_price]), count_len)
|
||||||
self._rt_ma2 = round(float(close_ma_array[-1]), self.round_n)
|
self._rt_ma2 = round(float(close_ma_array[-1]), self.round_n)
|
||||||
|
|
||||||
# 计算斜率
|
# 计算斜率
|
||||||
@ -1528,7 +1533,7 @@ class CtaLineBar(object):
|
|||||||
if self.para_ma3_len > 0:
|
if self.para_ma3_len > 0:
|
||||||
count_len = min(self.bar_len, self.para_ma3_len)
|
count_len = min(self.bar_len, self.para_ma3_len)
|
||||||
if count_len > 0:
|
if count_len > 0:
|
||||||
close_ma_array = ta.MA(np.append(self.close_array[-count_len:], [self.line_bar[-1].close]), count_len)
|
close_ma_array = ta.MA(np.append(self.close_array[-count_len:], [self.line_bar[-1].close_price]), count_len)
|
||||||
self._rt_ma3 = round(float(close_ma_array[-1]), self.round_n)
|
self._rt_ma3 = round(float(close_ma_array[-1]), self.round_n)
|
||||||
|
|
||||||
# 计算斜率
|
# 计算斜率
|
||||||
@ -1846,27 +1851,27 @@ class CtaLineBar(object):
|
|||||||
# 计算 ATR
|
# 计算 ATR
|
||||||
if self.para_atr1_len > 0:
|
if self.para_atr1_len > 0:
|
||||||
count_len = min(self.bar_len, self.para_atr1_len)
|
count_len = min(self.bar_len, self.para_atr1_len)
|
||||||
self.cur_atr1 = ta.ATR(self.high_array[-count_len:], self.low_array[-count_len:],
|
cur_atr1 = ta.ATR(self.high_array[-count_len * 2:], self.low_array[-count_len * 2:],
|
||||||
self.close_array[-count_len:], count_len)
|
self.close_array[-count_len * 2:], count_len)
|
||||||
self.cur_atr1 = round(self.cur_atr1, self.round_n)
|
self.cur_atr1 = round(cur_atr1[-1], self.round_n)
|
||||||
if len(self.line_atr1) > self.max_hold_bars:
|
if len(self.line_atr1) > self.max_hold_bars:
|
||||||
del self.line_atr1[0]
|
del self.line_atr1[0]
|
||||||
self.line_atr1.append(self.cur_atr1)
|
self.line_atr1.append(self.cur_atr1)
|
||||||
|
|
||||||
if self.para_atr2_len > 0:
|
if self.para_atr2_len > 0:
|
||||||
count_len = min(self.bar_len, self.para_atr2_len)
|
count_len = min(self.bar_len, self.para_atr2_len)
|
||||||
self.cur_atr2 = ta.ATR(self.high_array[-count_len:], self.low_array[-count_len:],
|
cur_atr2 = ta.ATR(self.high_array[-count_len * 2:], self.low_array[-count_len * 2:],
|
||||||
self.close_array[-count_len:], count_len)
|
self.close_array[-count_len * 2:], count_len)
|
||||||
self.cur_atr2 = round(self.cur_atr2, self.round_n)
|
self.cur_atr2 = round(cur_atr2[-1], self.round_n)
|
||||||
if len(self.line_atr2) > self.max_hold_bars:
|
if len(self.line_atr2) > self.max_hold_bars:
|
||||||
del self.line_atr2[0]
|
del self.line_atr2[0]
|
||||||
self.line_atr2.append(self.cur_atr2)
|
self.line_atr2.append(self.cur_atr2)
|
||||||
|
|
||||||
if self.para_atr3_len > 0:
|
if self.para_atr3_len > 0:
|
||||||
count_len = min(self.bar_len, self.para_atr3_len)
|
count_len = min(self.bar_len, self.para_atr3_len)
|
||||||
self.cur_atr3 = ta.ATR(self.high_array[-count_len:], self.low_array[-count_len:],
|
cur_atr3 = ta.ATR(self.high_array[-count_len * 2 :], self.low_array[-count_len * 2:],
|
||||||
self.close_array[-count_len:], count_len)
|
self.close_array[-count_len * 2:], count_len)
|
||||||
self.cur_atr3 = round(self.cur_atr3, self.round_n)
|
self.cur_atr3 = round(cur_atr3[-1], self.round_n)
|
||||||
|
|
||||||
if len(self.line_atr3) > self.max_hold_bars:
|
if len(self.line_atr3) > self.max_hold_bars:
|
||||||
del self.line_atr3[0]
|
del self.line_atr3[0]
|
||||||
@ -5398,424 +5403,5 @@ class CtaWeekBar(CtaLineBar):
|
|||||||
# 实时计算
|
# 实时计算
|
||||||
self.rt_executed = False
|
self.rt_executed = False
|
||||||
|
|
||||||
self.lastTick = tick
|
self.last_tick = tick
|
||||||
|
|
||||||
|
|
||||||
class test_strategy(object):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
|
|
||||||
self.price_tick = 1
|
|
||||||
self.underlying_symbol = 'I'
|
|
||||||
self.vt_symbol = 'I99'
|
|
||||||
|
|
||||||
self.lineM5 = None
|
|
||||||
self.lineM30 = None
|
|
||||||
self.lineH1 = None
|
|
||||||
self.lineH2 = None
|
|
||||||
self.lineD = None
|
|
||||||
self.lineW = None
|
|
||||||
|
|
||||||
self.TMinuteInterval = 1
|
|
||||||
|
|
||||||
self.save_m30_bars = []
|
|
||||||
self.save_h1_bars = []
|
|
||||||
self.save_h2_bars = []
|
|
||||||
self.save_d_bars = []
|
|
||||||
|
|
||||||
self.save_w_bars = []
|
|
||||||
|
|
||||||
def createM5(self):
|
|
||||||
"""使用ctalinbar,创建5分钟K线"""
|
|
||||||
lineM5Setting = {}
|
|
||||||
lineM5Setting['name'] = u'M5'
|
|
||||||
lineM5Setting['interval'] = Interval.MINUTE
|
|
||||||
lineM5Setting['bar_interval'] = 5
|
|
||||||
lineM5Setting['mode'] = CtaLineBar.TICK_MODE
|
|
||||||
lineM5Setting['price_tick'] = self.price_tick
|
|
||||||
lineM5Setting['underlying_symbol'] = self.underlying_symbol
|
|
||||||
self.lineM5 = CtaLineBar(self, self.onBarM5, lineM5Setting)
|
|
||||||
|
|
||||||
def onBarM5(self, bar):
|
|
||||||
self.write_log(self.lineM5.get_last_bar_str())
|
|
||||||
|
|
||||||
def createlineM30_with_macd(self):
|
|
||||||
"""使用CtaLineBar,创建30分钟时间"""
|
|
||||||
# 创建M30 K线
|
|
||||||
lineM30Setting = {}
|
|
||||||
lineM30Setting['name'] = u'M30'
|
|
||||||
lineM30Setting['interval'] = Interval.MINUTE
|
|
||||||
lineM30Setting['bar_interval'] = 30
|
|
||||||
lineM30Setting['para_macd_fast_len'] = 26
|
|
||||||
lineM30Setting['para_macd_slow_len'] = 12
|
|
||||||
lineM30Setting['para_macd_signal_len'] = 9
|
|
||||||
lineM30Setting['mode'] = CtaLineBar.TICK_MODE
|
|
||||||
lineM30Setting['price_tick'] = self.price_tick
|
|
||||||
lineM30Setting['underlying_symbol'] = self.underlying_symbol
|
|
||||||
self.lineM30 = CtaLineBar(self, self.onBarM30MACD, lineM30Setting)
|
|
||||||
|
|
||||||
def onBarM30MACD(self, bar):
|
|
||||||
self.write_log(self.lineM30.get_last_bar_str())
|
|
||||||
|
|
||||||
def createLineM30(self):
|
|
||||||
"""使用ctaMinuteBar, 测试内部自动写入csv文件"""
|
|
||||||
# 创建M30 K线
|
|
||||||
lineM30Setting = {}
|
|
||||||
lineM30Setting['name'] = u'M30'
|
|
||||||
lineM30Setting['interval'] = Interval.MINUTE
|
|
||||||
lineM30Setting['bar_interval'] = 30
|
|
||||||
lineM30Setting['para_pre_len'] = 10
|
|
||||||
lineM30Setting['para_ma1_len'] = 5
|
|
||||||
lineM30Setting['para_ma2_len'] = 10
|
|
||||||
lineM30Setting['para_ma3_len'] = 60
|
|
||||||
lineM30Setting['para_active_yb'] = True
|
|
||||||
lineM30Setting['para_active_skd'] = True
|
|
||||||
lineM30Setting['mode'] = CtaLineBar.TICK_MODE
|
|
||||||
lineM30Setting['price_tick'] = self.price_tick
|
|
||||||
lineM30Setting['underlying_symbol'] = self.underlying_symbol
|
|
||||||
self.lineM30 = CtaMinuteBar(self, self.onBarM30, lineM30Setting)
|
|
||||||
|
|
||||||
# 写入文件
|
|
||||||
self.lineM30.export_filename = os.path.abspath(
|
|
||||||
os.path.join(os.getcwd(),
|
|
||||||
u'export_{}_{}.csv'.format(self.vt_symbol, self.lineM30.name)))
|
|
||||||
|
|
||||||
self.lineM30.export_fields = [
|
|
||||||
{'name': 'datetime', 'source': 'bar', 'attr': 'datetime', 'type_': 'datetime'},
|
|
||||||
{'name': 'open', 'source': 'bar', 'attr': 'open_price', 'type_': 'float'},
|
|
||||||
{'name': 'high', 'source': 'bar', 'attr': 'high_price', 'type_': 'float'},
|
|
||||||
{'name': 'low', 'source': 'bar', 'attr': 'low_price', 'type_': 'float'},
|
|
||||||
{'name': 'close', 'source': 'bar', 'attr': 'close_price', 'type_': 'float'},
|
|
||||||
{'name': 'turnover', 'source': 'bar', 'attr': 'turnover', 'type_': 'float'},
|
|
||||||
{'name': 'volume', 'source': 'bar', 'attr': 'volume', 'type_': 'float'},
|
|
||||||
{'name': 'open_interest', 'source': 'bar', 'attr': 'open_interest', 'type_': 'float'},
|
|
||||||
{'name': 'kf', 'source': 'line_bar', 'attr': 'line_statemean', 'type_': 'list'}
|
|
||||||
]
|
|
||||||
|
|
||||||
def createLineH1(self):
|
|
||||||
# 创建2小时K线
|
|
||||||
lineH1Setting = {}
|
|
||||||
lineH1Setting['name'] = u'H1'
|
|
||||||
lineH1Setting['interval'] = Interval.HOUR
|
|
||||||
lineH1Setting['bar_interval'] = 1
|
|
||||||
lineH1Setting['para_pre_len'] = 10
|
|
||||||
lineH1Setting['para_ema1_len'] = 5
|
|
||||||
lineH1Setting['para_ema2_len'] = 10
|
|
||||||
lineH1Setting['para_ema3_len'] = 60
|
|
||||||
lineH1Setting['para_active_yb'] = True
|
|
||||||
lineH1Setting['para_active_skd'] = True
|
|
||||||
lineH1Setting['mode'] = CtaLineBar.TICK_MODE
|
|
||||||
lineH1Setting['price_tick'] = self.price_tick
|
|
||||||
lineH1Setting['underlying_symbol'] = self.underlying_symbol
|
|
||||||
self.lineH1 = CtaLineBar(self, self.onBarH1, lineH1Setting)
|
|
||||||
|
|
||||||
def createLineH2(self):
|
|
||||||
# 创建2小时K线
|
|
||||||
lineH2Setting = {}
|
|
||||||
lineH2Setting['name'] = u'H2'
|
|
||||||
lineH2Setting['interval'] = Interval.HOUR
|
|
||||||
lineH2Setting['bar_interval'] = 2
|
|
||||||
lineH2Setting['para_pre_len'] = 5
|
|
||||||
lineH2Setting['para_ma1_len'] = 5
|
|
||||||
lineH2Setting['para_ma2_len'] = 10
|
|
||||||
lineH2Setting['para_ma3_len'] = 18
|
|
||||||
lineH2Setting['para_active_yb'] = True
|
|
||||||
lineH2Setting['para_active_skd'] = True
|
|
||||||
lineH2Setting['mode'] = CtaLineBar.TICK_MODE
|
|
||||||
lineH2Setting['price_tick'] = self.price_tick
|
|
||||||
lineH2Setting['underlying_symbol'] = self.underlying_symbol
|
|
||||||
self.lineH2 = CtaHourBar(self, self.onBarH2, lineH2Setting)
|
|
||||||
|
|
||||||
def createLineD(self):
|
|
||||||
# 创建的日K线
|
|
||||||
lineDaySetting = {}
|
|
||||||
lineDaySetting['name'] = u'D1'
|
|
||||||
lineDaySetting['bar_interval'] = 1
|
|
||||||
lineDaySetting['para_pre_len'] = 5
|
|
||||||
lineDaySetting['para_art1_len'] = 26
|
|
||||||
lineDaySetting['para_ma1_len'] = 5
|
|
||||||
lineDaySetting['para_ma2_len'] = 10
|
|
||||||
lineDaySetting['para_ma3_len'] = 18
|
|
||||||
lineDaySetting['para_active_yb'] = True
|
|
||||||
lineDaySetting['para_active_skd'] = True
|
|
||||||
lineDaySetting['mode'] = CtaDayBar.TICK_MODE
|
|
||||||
lineDaySetting['price_tick'] = self.price_tick
|
|
||||||
lineDaySetting['underlying_symbol'] = self.underlying_symbol
|
|
||||||
self.lineD = CtaDayBar(self, self.onBarD, lineDaySetting)
|
|
||||||
|
|
||||||
def createLineW(self):
|
|
||||||
"""创建周线"""
|
|
||||||
lineWeekSetting = {}
|
|
||||||
lineWeekSetting['name'] = u'W1'
|
|
||||||
lineWeekSetting['para_pre_len'] = 5
|
|
||||||
lineWeekSetting['para_art1_len'] = 26
|
|
||||||
lineWeekSetting['para_ma1_len'] = 5
|
|
||||||
lineWeekSetting['para_ma2_len'] = 10
|
|
||||||
lineWeekSetting['para_ma3_len'] = 18
|
|
||||||
lineWeekSetting['para_active_yb'] = True
|
|
||||||
lineWeekSetting['para_active_skd'] = True
|
|
||||||
lineWeekSetting['mode'] = CtaDayBar.TICK_MODE
|
|
||||||
lineWeekSetting['price_tick'] = self.price_tick
|
|
||||||
lineWeekSetting['underlying_symbol'] = self.underlying_symbol
|
|
||||||
self.lineW = CtaWeekBar(self, self.onBarW, lineWeekSetting)
|
|
||||||
|
|
||||||
def onBar(self, bar):
|
|
||||||
# print(u'tradingDay:{},dt:{},o:{},h:{},l:{},c:{},v:{}'.format(bar.trading_day,bar.datetime, bar.open, bar.high, bar.low_price, bar.close_price, bar.volume))
|
|
||||||
if self.lineW:
|
|
||||||
self.lineW.add_bar(bar, bar_freq=self.TMinuteInterval)
|
|
||||||
if self.lineD:
|
|
||||||
self.lineD.add_bar(bar, bar_freq=self.TMinuteInterval)
|
|
||||||
if self.lineH2:
|
|
||||||
self.lineH2.add_bar(bar, bar_freq=self.TMinuteInterval)
|
|
||||||
|
|
||||||
if self.lineH1:
|
|
||||||
self.lineH1.add_bar(bar, bar_freq=self.TMinuteInterval)
|
|
||||||
|
|
||||||
if self.lineM30:
|
|
||||||
self.lineM30.add_bar(bar, bar_freq=self.TMinuteInterval)
|
|
||||||
|
|
||||||
if self.lineM5:
|
|
||||||
self.lineM5.add_bar(bar, bar_freq=self.TMinuteInterval)
|
|
||||||
|
|
||||||
# if self.lineH2:
|
|
||||||
# self.lineH2.skd_is_high_dead_cross(runtime=True, high_skd=30)
|
|
||||||
# self.lineH2.skd_is_low_golden_cross(runtime=True, low_skd=70)
|
|
||||||
|
|
||||||
def onBarM30(self, bar):
|
|
||||||
self.write_log(self.lineM30.get_last_bar_str())
|
|
||||||
|
|
||||||
self.save_m30_bars.append({
|
|
||||||
'datetime': bar.datetime,
|
|
||||||
'open': bar.open_price,
|
|
||||||
'high': bar.high_price,
|
|
||||||
'low': bar.low_price,
|
|
||||||
'close': bar.close_price,
|
|
||||||
'turnover': 0,
|
|
||||||
'volume': bar.volume,
|
|
||||||
'open_interest': 0,
|
|
||||||
'ma5': self.lineM30.line_ma1[-1] if len(self.lineM30.line_ma1) > 0 else bar.close_price,
|
|
||||||
'ma10': self.lineM30.line_ma2[-1] if len(self.lineM30.line_ma2) > 0 else bar.close_price,
|
|
||||||
'ma60': self.lineM30.line_ma3[-1] if len(self.lineM30.line_ma3) > 0 else bar.close_price,
|
|
||||||
'sk': self.lineM30.line_sk[-1] if len(self.lineM30.line_sk) > 0 else 0,
|
|
||||||
'sd': self.lineM30.line_sd[-1] if len(self.lineM30.line_sd) > 0 else 0
|
|
||||||
})
|
|
||||||
|
|
||||||
def onBarH1(self, bar):
|
|
||||||
self.write_log(self.lineH1.get_last_bar_str())
|
|
||||||
|
|
||||||
self.save_h1_bars.append({
|
|
||||||
'datetime': bar.datetime,
|
|
||||||
'open': bar.open_price,
|
|
||||||
'high': bar.high_price,
|
|
||||||
'low': bar.low_price,
|
|
||||||
'close': bar.close_price,
|
|
||||||
'turnover': 0,
|
|
||||||
'volume': bar.volume,
|
|
||||||
'open_interest': 0,
|
|
||||||
'ema5': self.lineH1.line_ema1[-1] if len(self.lineH1.line_ema1) > 0 else bar.close_price,
|
|
||||||
'ema10': self.lineH1.line_ema2[-1] if len(self.lineH1.line_ema2) > 0 else bar.close_price,
|
|
||||||
'ema60': self.lineH1.line_ema3[-1] if len(self.lineH1.line_ema3) > 0 else bar.close_price,
|
|
||||||
'sk': self.lineH1.line_sk[-1] if len(self.lineH1.line_sk) > 0 else 0,
|
|
||||||
'sd': self.lineH1.line_sd[-1] if len(self.lineH1.line_sd) > 0 else 0
|
|
||||||
})
|
|
||||||
|
|
||||||
def onBarH2(self, bar):
|
|
||||||
self.write_log(self.lineH2.get_last_bar_str())
|
|
||||||
|
|
||||||
self.save_h2_bars.append({
|
|
||||||
'datetime': bar.datetime,
|
|
||||||
'open': bar.open_price,
|
|
||||||
'high': bar.high_price,
|
|
||||||
'low': bar.low_price,
|
|
||||||
'close': bar.close_price,
|
|
||||||
'turnover': 0,
|
|
||||||
'volume': bar.volume,
|
|
||||||
'open_interest': 0,
|
|
||||||
'ma5': self.lineH2.line_ma1[-1] if len(self.lineH2.line_ma1) > 0 else bar.close_price,
|
|
||||||
'ma10': self.lineH2.line_ma2[-1] if len(self.lineH2.line_ma2) > 0 else bar.close_price,
|
|
||||||
'ma18': self.lineH2.line_ma3[-1] if len(self.lineH2.line_ma3) > 0 else bar.close_price,
|
|
||||||
'sk': self.lineH2.line_sk[-1] if len(self.lineH2.line_sk) > 0 else 0,
|
|
||||||
'sd': self.lineH2.line_sd[-1] if len(self.lineH2.line_sd) > 0 else 0
|
|
||||||
})
|
|
||||||
|
|
||||||
def onBarD(self, bar):
|
|
||||||
self.write_log(self.lineD.get_last_bar_str())
|
|
||||||
self.save_d_bars.append({
|
|
||||||
'datetime': bar.datetime,
|
|
||||||
'open': bar.open_price,
|
|
||||||
'high': bar.high_price,
|
|
||||||
'low': bar.low_price,
|
|
||||||
'close': bar.close_price,
|
|
||||||
'turnover': 0,
|
|
||||||
'volume': bar.volume,
|
|
||||||
'open_interest': 0,
|
|
||||||
'ma5': self.lineD.line_ma1[-1] if len(self.lineD.line_ma1) > 0 else bar.close_price,
|
|
||||||
'ma10': self.lineD.line_ma2[-1] if len(self.lineD.line_ma2) > 0 else bar.close_price,
|
|
||||||
'ma18': self.lineD.line_ma3[-1] if len(self.lineD.line_ma3) > 0 else bar.close_price,
|
|
||||||
'sk': self.lineD.line_sk[-1] if len(self.lineD.line_sk) > 0 else 0,
|
|
||||||
'sd': self.lineD.line_sd[-1] if len(self.lineD.line_sd) > 0 else 0
|
|
||||||
})
|
|
||||||
|
|
||||||
def onBarW(self, bar):
|
|
||||||
self.write_log(self.lineW.get_last_bar_str())
|
|
||||||
self.save_w_bars.append({
|
|
||||||
'datetime': bar.datetime,
|
|
||||||
'open': bar.open_price,
|
|
||||||
'high': bar.high_price,
|
|
||||||
'low': bar.low_price,
|
|
||||||
'close': bar.close_price,
|
|
||||||
'turnover': 0,
|
|
||||||
'volume': bar.volume,
|
|
||||||
'open_interest': 0,
|
|
||||||
'ma5': self.lineW.line_ma1[-1] if len(self.lineW.line_ma1) > 0 else bar.close_price,
|
|
||||||
'ma10': self.lineW.line_ma2[-1] if len(self.lineW.line_ma2) > 0 else bar.close_price,
|
|
||||||
'ma18': self.lineW.line_ma3[-1] if len(self.lineW.line_ma3) > 0 else bar.close_price,
|
|
||||||
'sk': self.lineW.line_sk[-1] if len(self.lineW.line_sk) > 0 else 0,
|
|
||||||
'sd': self.lineW.line_sd[-1] if len(self.lineW.line_sd) > 0 else 0
|
|
||||||
})
|
|
||||||
|
|
||||||
def on_tick(self, tick):
|
|
||||||
print(u'{0},{1},ap:{2},av:{3},bp:{4},bv:{5}'.format(tick.datetime, tick.last_price, tick.ask_price_1,
|
|
||||||
tick.ask_volume_1, tick.bid_price_1, tick.bid_volume_1))
|
|
||||||
|
|
||||||
def write_log(self, content):
|
|
||||||
print(content)
|
|
||||||
|
|
||||||
def saveData(self):
|
|
||||||
|
|
||||||
if len(self.save_m30_bars) > 0:
|
|
||||||
outputFile = '{}_m30.csv'.format(self.vt_symbol)
|
|
||||||
with open(outputFile, 'w', encoding='utf8', newline='') as f:
|
|
||||||
fieldnames = ['datetime', 'open', 'high', 'low', 'close', 'turnover', 'volume', 'open_interest',
|
|
||||||
'ma5', 'ma10', 'ma60', 'sk', 'sd']
|
|
||||||
writer = csv.DictWriter(f=f, fieldnames=fieldnames, dialect='excel')
|
|
||||||
writer.writeheader()
|
|
||||||
for row in self.save_m30_bars:
|
|
||||||
writer.writerow(row)
|
|
||||||
|
|
||||||
if len(self.save_h1_bars) > 0:
|
|
||||||
outputFile = '{}_h1.csv'.format(self.vt_symbol)
|
|
||||||
with open(outputFile, 'w', encoding='utf8', newline='') as f:
|
|
||||||
fieldnames = ['datetime', 'open', 'high', 'low', 'close', 'turnover', 'volume', 'open_interest',
|
|
||||||
'ema5', 'ema10', 'ema60', 'sk', 'sd']
|
|
||||||
writer = csv.DictWriter(f=f, fieldnames=fieldnames, dialect='excel')
|
|
||||||
writer.writeheader()
|
|
||||||
for row in self.save_h1_bars:
|
|
||||||
writer.writerow(row)
|
|
||||||
|
|
||||||
if len(self.save_h2_bars) > 0:
|
|
||||||
outputFile = '{}_h2.csv'.format(self.vt_symbol)
|
|
||||||
with open(outputFile, 'w', encoding='utf8', newline='') as f:
|
|
||||||
fieldnames = ['datetime', 'open', 'high', 'low', 'close', 'turnover', 'volume', 'open_interest',
|
|
||||||
'ma5', 'ma10', 'ma18', 'sk', 'sd']
|
|
||||||
writer = csv.DictWriter(f=f, fieldnames=fieldnames, dialect='excel')
|
|
||||||
writer.writeheader()
|
|
||||||
for row in self.save_h2_bars:
|
|
||||||
writer.writerow(row)
|
|
||||||
|
|
||||||
if len(self.save_d_bars) > 0:
|
|
||||||
outputFile = '{}_d.csv'.format(self.vt_symbol)
|
|
||||||
with open(outputFile, 'w', encoding='utf8', newline='') as f:
|
|
||||||
fieldnames = ['datetime', 'open', 'high', 'low', 'close', 'turnover', 'volume', 'open_interest',
|
|
||||||
'ma5', 'ma10', 'ma18', 'sk', 'sd']
|
|
||||||
writer = csv.DictWriter(f=f, fieldnames=fieldnames, dialect='excel')
|
|
||||||
writer.writeheader()
|
|
||||||
for row in self.save_d_bars:
|
|
||||||
writer.writerow(row)
|
|
||||||
|
|
||||||
if len(self.save_w_bars) > 0:
|
|
||||||
outputFile = '{}_w.csv'.format(self.vt_symbol)
|
|
||||||
with open(outputFile, 'w', encoding='utf8', newline='') as f:
|
|
||||||
fieldnames = ['datetime', 'open', 'high', 'low', 'close', 'turnover', 'volume', 'open_interest',
|
|
||||||
'ma5', 'ma10', 'ma18', 'sk', 'sd']
|
|
||||||
writer = csv.DictWriter(f=f, fieldnames=fieldnames, dialect='excel')
|
|
||||||
writer.writeheader()
|
|
||||||
for row in self.save_w_bars:
|
|
||||||
writer.writerow(row)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
t = test_strategy()
|
|
||||||
t.price_tick = 0.5
|
|
||||||
t.underlying_symbol = 'J'
|
|
||||||
t.vt_symbol = 'J99'
|
|
||||||
|
|
||||||
# t.createM5()
|
|
||||||
# t.createLineW()
|
|
||||||
|
|
||||||
# t.createlineM30_with_macd()
|
|
||||||
|
|
||||||
# 创建M30线
|
|
||||||
# t.createLineM30()
|
|
||||||
|
|
||||||
# 回测1小时线
|
|
||||||
# t.createLineH1()
|
|
||||||
|
|
||||||
# 回测2小时线
|
|
||||||
# t.createLineH2()
|
|
||||||
|
|
||||||
# 回测日线
|
|
||||||
# t.createLineD()
|
|
||||||
|
|
||||||
# 测试周线
|
|
||||||
t.createLineW()
|
|
||||||
|
|
||||||
# vnpy/app/cta_strategy_pro/
|
|
||||||
vnpy_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
|
||||||
|
|
||||||
filename = os.path.abspath(os.path.join(vnpy_root, 'bar_data/{}_20160101_20190517_1m.csv'.format(t.vt_symbol)))
|
|
||||||
csv_bar_seconds = 60 # csv 文件内,bar的时间间隔60秒
|
|
||||||
|
|
||||||
import csv
|
|
||||||
|
|
||||||
csvfile = open(filename, 'r', encoding='utf8')
|
|
||||||
reader = csv.DictReader((line.replace('\0', '') for line in csvfile), delimiter=",")
|
|
||||||
last_tradingDay = None
|
|
||||||
for row in reader:
|
|
||||||
try:
|
|
||||||
dt = datetime.strptime(row['index'], '%Y-%m-%d %H:%M:%S') - timedelta(seconds=csv_bar_seconds)
|
|
||||||
|
|
||||||
bar = BarData(
|
|
||||||
gateway_name='',
|
|
||||||
symbol=t.vt_symbol,
|
|
||||||
exchange=Exchange.LOCAL,
|
|
||||||
datetime=dt,
|
|
||||||
interval=Interval.MINUTE,
|
|
||||||
open_price=round_to(float(row['open']), t.price_tick),
|
|
||||||
high_price=round_to(float(row['high']), t.price_tick),
|
|
||||||
low_price=round_to(float(row['low']), t.price_tick),
|
|
||||||
close_price=round_to(float(row['close']), t.price_tick),
|
|
||||||
volume=float(row['volume'])
|
|
||||||
)
|
|
||||||
|
|
||||||
if 'trading_date' in row:
|
|
||||||
bar.trading_day = row['trading_date']
|
|
||||||
if len(bar.trading_day) == 8 and '-' not in bar.trading_day:
|
|
||||||
bar.trading_day = bar.trading_day[0:4] + '-' + bar.trading_day[4:6] + '-' + bar.trading_day[6:8]
|
|
||||||
else:
|
|
||||||
if bar.datetime.hour >= 21:
|
|
||||||
if bar.datetime.isoweekday() == 5:
|
|
||||||
# 星期五=》星期一
|
|
||||||
bar.trading_day = (dt + timedelta(days=3)).strftime('%Y-%m-%d')
|
|
||||||
else:
|
|
||||||
# 第二天
|
|
||||||
bar.trading_day = (dt + timedelta(days=1)).strftime('%Y-%m-%d')
|
|
||||||
elif bar.datetime.hour < 8 and bar.datetime.isoweekday() == 6:
|
|
||||||
# 星期六=>星期一
|
|
||||||
bar.trading_day = (dt + timedelta(days=2)).strftime('%Y-%m-%d')
|
|
||||||
else:
|
|
||||||
bar.trading_day = bar.datetime.strftime('%Y-%m-%d')
|
|
||||||
|
|
||||||
t.onBar(bar)
|
|
||||||
# 测试 实时计算值
|
|
||||||
# sk, sd = t.lineM30.getRuntimeSKD()
|
|
||||||
|
|
||||||
# 测试实时计算值
|
|
||||||
# if bar.datetime.minute==1:
|
|
||||||
# print('rt_Dif:{}'.format(t.lineM30.rt_Dif))
|
|
||||||
except Exception as ex:
|
|
||||||
t.write_log(u'{0}:{1}'.format(Exception, ex))
|
|
||||||
traceback.print_exc()
|
|
||||||
break
|
|
||||||
|
|
||||||
t.saveData()
|
|
||||||
|
@ -4,7 +4,7 @@ import os
|
|||||||
import json
|
import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from vnpy.app.cta_strategy_pro.template import CtaComponent
|
from vnpy.app.cta_strategy_pro.base import CtaComponent
|
||||||
from vnpy.trader.utility import get_folder_path
|
from vnpy.trader.utility import get_folder_path
|
||||||
|
|
||||||
|
|
||||||
@ -18,12 +18,12 @@ class CtaPolicy(CtaComponent):
|
|||||||
构造
|
构造
|
||||||
:param strategy:
|
:param strategy:
|
||||||
"""
|
"""
|
||||||
super(CtaPolicy).__init__(strategy=strategy)
|
super(CtaPolicy,self).__init__(strategy=strategy, kwargs=kwargs)
|
||||||
|
|
||||||
self.create_time = None
|
self.create_time = None
|
||||||
self.save_time = None
|
self.save_time = None
|
||||||
|
|
||||||
def toJson(self):
|
def to_json(self):
|
||||||
"""
|
"""
|
||||||
将数据转换成dict
|
将数据转换成dict
|
||||||
datetime =》 string
|
datetime =》 string
|
||||||
@ -36,7 +36,7 @@ class CtaPolicy(CtaComponent):
|
|||||||
|
|
||||||
return j
|
return j
|
||||||
|
|
||||||
def fromJson(self, json_data):
|
def from_json(self, json_data):
|
||||||
"""
|
"""
|
||||||
将数据从json_data中恢复
|
将数据从json_data中恢复
|
||||||
:param json_data:
|
:param json_data:
|
||||||
@ -67,7 +67,7 @@ class CtaPolicy(CtaComponent):
|
|||||||
从持久化文件中获取
|
从持久化文件中获取
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
json_file = os.path.abspath(os.path.join(get_folder_path('data'), u'{}_Policy.json'.format(self.strategy.name)))
|
json_file = os.path.abspath(os.path.join(get_folder_path('data'), u'{}_Policy.json'.format(self.strategy.strategy_name)))
|
||||||
|
|
||||||
json_data = {}
|
json_data = {}
|
||||||
if os.path.exists(json_file):
|
if os.path.exists(json_file):
|
||||||
@ -80,7 +80,7 @@ class CtaPolicy(CtaComponent):
|
|||||||
json_data = {}
|
json_data = {}
|
||||||
|
|
||||||
# 从持久化文件恢复数据
|
# 从持久化文件恢复数据
|
||||||
self.fromJson(json_data)
|
self.from_json(json_data)
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
"""
|
"""
|
||||||
@ -88,14 +88,14 @@ class CtaPolicy(CtaComponent):
|
|||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
json_file = os.path.abspath(
|
json_file = os.path.abspath(
|
||||||
os.path.join(get_folder_path('data'), u'{}_Policy.json'.format(self.strategy.name)))
|
os.path.join(get_folder_path('data'), u'{}_Policy.json'.format(self.strategy.strategy_name)))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 修改为:回测时不保存
|
# 修改为:回测时不保存
|
||||||
if self.strategy and self.strategy.backtesting:
|
if self.strategy and self.strategy.backtesting:
|
||||||
return
|
return
|
||||||
|
|
||||||
json_data = self.toJson()
|
json_data = self.to_json()
|
||||||
json_data['save_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
json_data['save_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||||
with open(json_file, 'w') as f:
|
with open(json_file, 'w') as f:
|
||||||
data = json.dumps(json_data, indent=4)
|
data = json.dumps(json_data, indent=4)
|
||||||
|
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from vnpy.app.cta_strategy_pro.base import Direction
|
from vnpy.app.cta_strategy_pro.base import Direction, CtaComponent
|
||||||
from vnpy.app.cta_strategy_pro.template import CtaComponent
|
|
||||||
|
|
||||||
|
|
||||||
class CtaPosition(CtaComponent):
|
class CtaPosition(CtaComponent):
|
||||||
@ -15,13 +14,13 @@ class CtaPosition(CtaComponent):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, strategy, **kwargs):
|
def __init__(self, strategy, **kwargs):
|
||||||
super(CtaComponent).__init__(strategy=strategy)
|
super(CtaPosition, self).__init__(strategy=strategy, kwargs=kwargs)
|
||||||
self.long_pos = 0 # 多仓持仓(正数)
|
self.long_pos = 0 # 多仓持仓(正数)
|
||||||
self.short_pos = 0 # 空仓持仓(负数)
|
self.short_pos = 0 # 空仓持仓(负数)
|
||||||
self.pos = 0 # 持仓状态 0:空仓/对空平等; >=1 净多仓 ;<=-1 净空仓
|
self.pos = 0 # 持仓状态 0:空仓/对空平等; >=1 净多仓 ;<=-1 净空仓
|
||||||
self.maxPos = sys.maxsize # 最大持仓量(多仓+空仓总量)
|
self.maxPos = sys.maxsize # 最大持仓量(多仓+空仓总量)
|
||||||
|
|
||||||
def open_pos(self, direction: Direction, volume: int):
|
def open_pos(self, direction: Direction, volume: float):
|
||||||
"""开、加仓"""
|
"""开、加仓"""
|
||||||
# volume: 正整数
|
# volume: 正整数
|
||||||
|
|
||||||
@ -31,7 +30,7 @@ class CtaPosition(CtaComponent):
|
|||||||
|
|
||||||
# 更新
|
# 更新
|
||||||
self.write_log(f'多仓:{self.long_pos}->{self.long_pos + volume}')
|
self.write_log(f'多仓:{self.long_pos}->{self.long_pos + volume}')
|
||||||
self.write_log(u'净:{self.pos}->{self.pos + volume}')
|
self.write_log(f'净:{self.pos}->{self.pos + volume}')
|
||||||
self.long_pos += volume
|
self.long_pos += volume
|
||||||
self.pos += volume
|
self.pos += volume
|
||||||
|
|
||||||
@ -40,13 +39,13 @@ class CtaPosition(CtaComponent):
|
|||||||
self.write_error(content=f'开仓异常,净:{self.pos},空:{self.short_pos},加空:{volume},超过:{self.maxPos}')
|
self.write_error(content=f'开仓异常,净:{self.pos},空:{self.short_pos},加空:{volume},超过:{self.maxPos}')
|
||||||
|
|
||||||
self.write_log(f'空仓:{self.short_pos}->{self.short_pos - volume}')
|
self.write_log(f'空仓:{self.short_pos}->{self.short_pos - volume}')
|
||||||
self.write_log(u'净:{self.pos}->{self.pos - volume}')
|
self.write_log(f'净:{self.pos}->{self.pos - volume}')
|
||||||
self.short_pos -= volume
|
self.short_pos -= volume
|
||||||
self.pos -= volume
|
self.pos -= volume
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def close_pos(self, direction: Direction, volume):
|
def close_pos(self, direction: Direction, volume:float):
|
||||||
"""平、减仓"""
|
"""平、减仓"""
|
||||||
# vol: 正整数
|
# vol: 正整数
|
||||||
|
|
||||||
|
@ -43,13 +43,17 @@ from vnpy.trader.constant import (
|
|||||||
Status
|
Status
|
||||||
)
|
)
|
||||||
from vnpy.trader.utility import (
|
from vnpy.trader.utility import (
|
||||||
load_json, save_json,
|
load_json,
|
||||||
|
save_json,
|
||||||
extract_vt_symbol,
|
extract_vt_symbol,
|
||||||
round_to, get_folder_path,
|
round_to,
|
||||||
|
TRADER_DIR,
|
||||||
|
get_folder_path,
|
||||||
get_underlying_symbol,
|
get_underlying_symbol,
|
||||||
append_data)
|
append_data)
|
||||||
|
|
||||||
from vnpy.trader.util_logger import setup_logger, logging
|
from vnpy.trader.util_logger import setup_logger, logging
|
||||||
|
from vnpy.trader.util_wechat import send_wx_msg
|
||||||
from vnpy.trader.converter import OffsetConverter
|
from vnpy.trader.converter import OffsetConverter
|
||||||
|
|
||||||
from .base import (
|
from .base import (
|
||||||
@ -88,7 +92,6 @@ class CtaEngine(BaseEngine):
|
|||||||
6、支持指定gateway的交易。主引擎可接入多个gateway
|
6、支持指定gateway的交易。主引擎可接入多个gateway
|
||||||
"""
|
"""
|
||||||
|
|
||||||
engine_type = EngineType.LIVE # live trading engine
|
|
||||||
engine_type = EngineType.LIVE # live trading engine
|
engine_type = EngineType.LIVE # live trading engine
|
||||||
|
|
||||||
# 策略配置文件
|
# 策略配置文件
|
||||||
@ -264,13 +267,17 @@ class CtaEngine(BaseEngine):
|
|||||||
# strategy.pos -= trade.volume
|
# strategy.pos -= trade.volume
|
||||||
# 根据策略名称,写入 data\straetgy_name_trade.csv文件
|
# 根据策略名称,写入 data\straetgy_name_trade.csv文件
|
||||||
strategy_name = getattr(strategy, 'name')
|
strategy_name = getattr(strategy, 'name')
|
||||||
trade_fields = ['time', 'symbol', 'exchange', 'vt_symbol', 'tradeid', 'vt_tradeid', 'orderid', 'vt_orderid',
|
trade_fields = ['datetime', 'symbol', 'exchange', 'vt_symbol', 'tradeid', 'vt_tradeid', 'orderid', 'vt_orderid',
|
||||||
'direction', 'offset', 'price', 'volume', 'idx_price']
|
'direction', 'offset', 'price', 'volume', 'idx_price']
|
||||||
trade_dict = OrderedDict()
|
trade_dict = OrderedDict()
|
||||||
try:
|
try:
|
||||||
for k in trade_fields:
|
for k in trade_fields:
|
||||||
if k == 'time':
|
if k == 'datetime':
|
||||||
trade_dict[k] = datetime.now().strftime('%Y-%m-%d') + ' ' + getattr(trade, k, '')
|
dt = getattr(trade, 'datetime')
|
||||||
|
if isinstance(dt, datetime):
|
||||||
|
trade_dict[k] = dt.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
else:
|
||||||
|
trade_dict[k] = datetime.now().strftime('%Y-%m-%d') + ' ' + getattr(trade, 'time', '')
|
||||||
if k in ['exchange', 'direction', 'offset']:
|
if k in ['exchange', 'direction', 'offset']:
|
||||||
trade_dict[k] = getattr(trade, k).value
|
trade_dict[k] = getattr(trade, k).value
|
||||||
else:
|
else:
|
||||||
@ -586,12 +593,12 @@ class CtaEngine(BaseEngine):
|
|||||||
order = self.main_engine.get_order(vt_orderid)
|
order = self.main_engine.get_order(vt_orderid)
|
||||||
if not order:
|
if not order:
|
||||||
self.write_log(msg=f"撤单失败,找不到委托{vt_orderid}",
|
self.write_log(msg=f"撤单失败,找不到委托{vt_orderid}",
|
||||||
strategy_Name=strategy.name,
|
strategy_Name=strategy.strategy_name,
|
||||||
level=logging.ERROR)
|
level=logging.ERROR)
|
||||||
return
|
return False
|
||||||
|
|
||||||
req = order.create_cancel_request()
|
req = order.create_cancel_request()
|
||||||
self.main_engine.cancel_order(req, order.gateway_name)
|
return self.main_engine.cancel_order(req, order.gateway_name)
|
||||||
|
|
||||||
def cancel_local_stop_order(self, strategy: CtaTemplate, stop_orderid: str):
|
def cancel_local_stop_order(self, strategy: CtaTemplate, stop_orderid: str):
|
||||||
"""
|
"""
|
||||||
@ -599,7 +606,7 @@ class CtaEngine(BaseEngine):
|
|||||||
"""
|
"""
|
||||||
stop_order = self.stop_orders.get(stop_orderid, None)
|
stop_order = self.stop_orders.get(stop_orderid, None)
|
||||||
if not stop_order:
|
if not stop_order:
|
||||||
return
|
return False
|
||||||
strategy = self.strategies[stop_order.strategy_name]
|
strategy = self.strategies[stop_order.strategy_name]
|
||||||
|
|
||||||
# Remove from relation map.
|
# Remove from relation map.
|
||||||
@ -614,6 +621,7 @@ class CtaEngine(BaseEngine):
|
|||||||
|
|
||||||
self.call_strategy_func(strategy, strategy.on_stop_order, stop_order)
|
self.call_strategy_func(strategy, strategy.on_stop_order, stop_order)
|
||||||
self.put_stop_order_event(stop_order)
|
self.put_stop_order_event(stop_order)
|
||||||
|
return True
|
||||||
|
|
||||||
def send_order(
|
def send_order(
|
||||||
self,
|
self,
|
||||||
@ -634,7 +642,7 @@ class CtaEngine(BaseEngine):
|
|||||||
contract = self.main_engine.get_contract(vt_symbol)
|
contract = self.main_engine.get_contract(vt_symbol)
|
||||||
if not contract:
|
if not contract:
|
||||||
self.write_log(msg=f"委托失败,找不到合约:{vt_symbol}",
|
self.write_log(msg=f"委托失败,找不到合约:{vt_symbol}",
|
||||||
strategy_name=strategy.name,
|
strategy_name=strategy.strategy_name,
|
||||||
level=logging.ERROR)
|
level=logging.ERROR)
|
||||||
return ""
|
return ""
|
||||||
if contract.gateway_name and not gateway_name:
|
if contract.gateway_name and not gateway_name:
|
||||||
@ -659,9 +667,9 @@ class CtaEngine(BaseEngine):
|
|||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
if vt_orderid.startswith(STOPORDER_PREFIX):
|
if vt_orderid.startswith(STOPORDER_PREFIX):
|
||||||
self.cancel_local_stop_order(strategy, vt_orderid)
|
return self.cancel_local_stop_order(strategy, vt_orderid)
|
||||||
else:
|
else:
|
||||||
self.cancel_server_order(strategy, vt_orderid)
|
return self.cancel_server_order(strategy, vt_orderid)
|
||||||
|
|
||||||
def cancel_all(self, strategy: CtaTemplate):
|
def cancel_all(self, strategy: CtaTemplate):
|
||||||
"""
|
"""
|
||||||
@ -689,7 +697,7 @@ class CtaEngine(BaseEngine):
|
|||||||
self.main_engine.subscribe(req, gateway_name)
|
self.main_engine.subscribe(req, gateway_name)
|
||||||
else:
|
else:
|
||||||
self.write_log(msg=f"找不到合约{vt_symbol},添加到待订阅列表",
|
self.write_log(msg=f"找不到合约{vt_symbol},添加到待订阅列表",
|
||||||
strategy_name=strategy.name)
|
strategy_name=strategy.strategy_name)
|
||||||
self.pending_subcribe_symbol_map[f'{gateway_name}.{vt_symbol}'].add((strategy_name, is_bar))
|
self.pending_subcribe_symbol_map[f'{gateway_name}.{vt_symbol}'].add((strategy_name, is_bar))
|
||||||
try:
|
try:
|
||||||
self.write_log(f'找不到合约{vt_symbol}信息,尝试请求所有接口')
|
self.write_log(f'找不到合约{vt_symbol}信息,尝试请求所有接口')
|
||||||
@ -713,7 +721,7 @@ class CtaEngine(BaseEngine):
|
|||||||
strategies.append(strategy)
|
strategies.append(strategy)
|
||||||
|
|
||||||
# 添加 策略名 strategy_name <=> 合约订阅 vt_symbol 的映射
|
# 添加 策略名 strategy_name <=> 合约订阅 vt_symbol 的映射
|
||||||
subscribe_symbol_set = self.strategy_symbol_map[strategy.name]
|
subscribe_symbol_set = self.strategy_symbol_map[strategy.strategy_name]
|
||||||
subscribe_symbol_set.add(contract.vt_symbol)
|
subscribe_symbol_set.add(contract.vt_symbol)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@ -786,6 +794,16 @@ class CtaEngine(BaseEngine):
|
|||||||
""""""
|
""""""
|
||||||
return self.engine_type
|
return self.engine_type
|
||||||
|
|
||||||
|
@lru_cache()
|
||||||
|
def get_data_path(self):
|
||||||
|
data_path = os.path.abspath(os.path.join(TRADER_DIR, 'data'))
|
||||||
|
return data_path
|
||||||
|
|
||||||
|
@lru_cache()
|
||||||
|
def get_logs_path(self):
|
||||||
|
log_path = os.path.abspath(os.path.join(TRADER_DIR, 'log'))
|
||||||
|
return log_path
|
||||||
|
|
||||||
def call_strategy_func(
|
def call_strategy_func(
|
||||||
self, strategy: CtaTemplate, func: Callable, params: Any = None
|
self, strategy: CtaTemplate, func: Callable, params: Any = None
|
||||||
):
|
):
|
||||||
@ -1389,3 +1407,17 @@ class CtaEngine(BaseEngine):
|
|||||||
subject = "CTA策略引擎"
|
subject = "CTA策略引擎"
|
||||||
|
|
||||||
self.main_engine.send_email(subject, msg)
|
self.main_engine.send_email(subject, msg)
|
||||||
|
|
||||||
|
def send_wechat(self, msg: str, strategy: CtaTemplate = None):
|
||||||
|
"""
|
||||||
|
send wechat message to default receiver
|
||||||
|
:param msg:
|
||||||
|
:param strategy:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if strategy:
|
||||||
|
subject = f"{strategy.strategy_name}"
|
||||||
|
else:
|
||||||
|
subject = "CTAPRO引擎"
|
||||||
|
|
||||||
|
send_wx_msg(content=f'{subject}:{msg}')
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -33,7 +33,7 @@ class TurtleSignalStrategy(CtaTemplate):
|
|||||||
long_stop = 0
|
long_stop = 0
|
||||||
short_stop = 0
|
short_stop = 0
|
||||||
|
|
||||||
parameters = ["x_minuite", "entry_window", "exit_window", "atr_window", "fixed_size"]
|
parameters = ["x_minute", "entry_window", "exit_window", "atr_window", "fixed_size"]
|
||||||
variables = ["entry_up", "entry_down", "exit_up", "exit_down", "atr_value"]
|
variables = ["entry_up", "entry_down", "exit_up", "exit_down", "atr_value"]
|
||||||
|
|
||||||
def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
|
def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
|
||||||
@ -52,7 +52,7 @@ class TurtleSignalStrategy(CtaTemplate):
|
|||||||
Callback when strategy is inited.
|
Callback when strategy is inited.
|
||||||
"""
|
"""
|
||||||
self.write_log("策略初始化")
|
self.write_log("策略初始化")
|
||||||
#self.load_bar(20)
|
# self.load_bar(20)
|
||||||
|
|
||||||
def on_start(self):
|
def on_start(self):
|
||||||
"""
|
"""
|
||||||
|
@ -11,6 +11,8 @@ from vnpy.app.cta_strategy_pro import (
|
|||||||
ArrayManager,
|
ArrayManager,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from vnpy.trader.utility import round_to
|
||||||
|
|
||||||
|
|
||||||
class TurtleSignalStrategy_v2(CtaTemplate):
|
class TurtleSignalStrategy_v2(CtaTemplate):
|
||||||
""""""
|
""""""
|
||||||
@ -35,7 +37,7 @@ class TurtleSignalStrategy_v2(CtaTemplate):
|
|||||||
long_stop = 0
|
long_stop = 0
|
||||||
short_stop = 0
|
short_stop = 0
|
||||||
|
|
||||||
parameters = ["x_minuite", "entry_window", "exit_window", "atr_window", "fixed_size"]
|
parameters = ["x_minute", "entry_window", "exit_window", "atr_window", "fixed_size"]
|
||||||
variables = ["entry_up", "entry_down", "exit_up", "exit_down", "atr_value"]
|
variables = ["entry_up", "entry_down", "exit_up", "exit_down", "atr_value"]
|
||||||
|
|
||||||
def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
|
def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
|
||||||
@ -47,6 +49,7 @@ class TurtleSignalStrategy_v2(CtaTemplate):
|
|||||||
# 获取合约乘数,保证金比例
|
# 获取合约乘数,保证金比例
|
||||||
self.symbol_size = self.cta_engine.get_size(self.vt_symbol)
|
self.symbol_size = self.cta_engine.get_size(self.vt_symbol)
|
||||||
self.symbol_margin_rate = self.cta_engine.get_margin_rate(self.vt_symbol)
|
self.symbol_margin_rate = self.cta_engine.get_margin_rate(self.vt_symbol)
|
||||||
|
self.symbol_price_tick = self.cta_engine.get_price_tick(self.vt_symbol)
|
||||||
|
|
||||||
self.bg = BarGenerator(self.on_bar, window=self.x_minute)
|
self.bg = BarGenerator(self.on_bar, window=self.x_minute)
|
||||||
self.am = ArrayManager()
|
self.am = ArrayManager()
|
||||||
@ -58,7 +61,7 @@ class TurtleSignalStrategy_v2(CtaTemplate):
|
|||||||
Callback when strategy is inited.
|
Callback when strategy is inited.
|
||||||
"""
|
"""
|
||||||
self.write_log("策略初始化")
|
self.write_log("策略初始化")
|
||||||
#self.load_bar(20)
|
# self.load_bar(20)
|
||||||
|
|
||||||
def on_start(self):
|
def on_start(self):
|
||||||
"""
|
"""
|
||||||
@ -98,8 +101,12 @@ class TurtleSignalStrategy_v2(CtaTemplate):
|
|||||||
|
|
||||||
self.exit_up, self.exit_down = self.am.donchian(self.exit_window)
|
self.exit_up, self.exit_down = self.am.donchian(self.exit_window)
|
||||||
|
|
||||||
|
if bar.datetime.strftime('%Y-%m-%d %H') == '2016-03-07 09':
|
||||||
|
a = 1 # noqa
|
||||||
|
|
||||||
if not self.pos:
|
if not self.pos:
|
||||||
self.atr_value = self.am.atr(self.atr_window)
|
self.atr_value = self.am.atr(self.atr_window)
|
||||||
|
self.atr_value = max(4 * self.symbol_price_tick, self.atr_value)
|
||||||
|
|
||||||
self.long_entry = 0
|
self.long_entry = 0
|
||||||
self.short_entry = 0
|
self.short_entry = 0
|
||||||
@ -112,13 +119,17 @@ class TurtleSignalStrategy_v2(CtaTemplate):
|
|||||||
self.send_buy_orders(self.entry_up)
|
self.send_buy_orders(self.entry_up)
|
||||||
|
|
||||||
sell_price = max(self.long_stop, self.exit_down)
|
sell_price = max(self.long_stop, self.exit_down)
|
||||||
self.sell(sell_price, abs(self.pos), True)
|
refs = self.sell(sell_price, abs(self.pos), True)
|
||||||
|
if len(refs) > 0:
|
||||||
|
self.write_log(f'平多委托编号:{refs}')
|
||||||
|
|
||||||
elif self.pos < 0:
|
elif self.pos < 0:
|
||||||
self.send_short_orders(self.entry_down)
|
self.send_short_orders(self.entry_down)
|
||||||
|
|
||||||
cover_price = min(self.short_stop, self.exit_up)
|
cover_price = min(self.short_stop, self.exit_up)
|
||||||
ret = self.cover(cover_price, abs(self.pos), True)
|
refs = self.cover(cover_price, abs(self.pos), True)
|
||||||
|
if len(refs) > 0:
|
||||||
|
self.write_log(f'平空委托编号:{refs}')
|
||||||
|
|
||||||
self.put_event()
|
self.put_event()
|
||||||
|
|
||||||
@ -161,48 +172,77 @@ class TurtleSignalStrategy_v2(CtaTemplate):
|
|||||||
|
|
||||||
def send_buy_orders(self, price):
|
def send_buy_orders(self, price):
|
||||||
""""""
|
""""""
|
||||||
if self.pos >= 4:
|
|
||||||
return
|
|
||||||
|
|
||||||
if self.cur_mi_price <= price - self.atr_value/2:
|
if self.cur_mi_price <= price - self.atr_value / 2:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.update_invest_pos()
|
self.update_invest_pos()
|
||||||
|
|
||||||
t = self.pos / self.invest_pos
|
t = int(self.pos / self.invest_pos)
|
||||||
|
|
||||||
|
if t >= 4:
|
||||||
|
return
|
||||||
|
|
||||||
if t < 1:
|
if t < 1:
|
||||||
self.buy(price, self.invest_pos, True)
|
refs = self.buy(price, self.invest_pos, True)
|
||||||
|
if len(refs) > 0:
|
||||||
|
self.write_log(f'买入委托编号:{refs}')
|
||||||
|
|
||||||
if t < 2:
|
if t == 1 and self.cur_mi_price > price:
|
||||||
self.buy(price + self.atr_value * 0.5, self.invest_pos, True)
|
buy_price = round_to(price + self.atr_value * 0.5 , self.symbol_price_tick)
|
||||||
|
self.write_log(u'发出做多停止单,触发价格为: {}'.format(buy_price))
|
||||||
|
refs = self.buy(buy_price, self.invest_pos, True)
|
||||||
|
if len(refs) > 0:
|
||||||
|
self.write_log(f'买入委托编号:{refs}')
|
||||||
|
|
||||||
if t < 3:
|
if t == 2 and self.cur_mi_price > price + self.atr_value * 0.5:
|
||||||
self.buy(price + self.atr_value, self.invest_pos, True)
|
buy_price = round_to(price + self.atr_value, self.symbol_price_tick)
|
||||||
|
self.write_log(u'发出做多停止单,触发价格为: {}'.format(buy_price))
|
||||||
|
refs = self.buy(buy_price, self.invest_pos, True)
|
||||||
|
if len(refs) > 0:
|
||||||
|
self.write_log(f'买入委托编号:{refs}')
|
||||||
|
|
||||||
if t < 4:
|
if t == 3 and self.cur_mi_price > price + self.atr_value:
|
||||||
self.buy(price + self.atr_value * 1.5, self.invest_pos, True)
|
buy_price = round_to(price + self.atr_value * 1.5, self.symbol_price_tick)
|
||||||
|
self.write_log(u'发出做多停止单,触发价格为: {}'.format(buy_price))
|
||||||
|
refs = self.buy(buy_price, self.invest_pos, True)
|
||||||
|
if len(refs) > 0:
|
||||||
|
self.write_log(f'买入委托编号:{refs}')
|
||||||
|
|
||||||
def send_short_orders(self, price):
|
def send_short_orders(self, price):
|
||||||
""""""
|
""""""
|
||||||
if self.pos <= -4:
|
|
||||||
return
|
|
||||||
|
|
||||||
if self.cur_mi_price >= price + self.atr_value / 2:
|
if self.cur_mi_price >= price + self.atr_value / 2:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.update_invest_pos()
|
self.update_invest_pos()
|
||||||
|
|
||||||
t = self.pos / self.invest_pos
|
t = int(self.pos / self.invest_pos)
|
||||||
|
|
||||||
|
if t <= -4:
|
||||||
|
return
|
||||||
|
|
||||||
if t > -1:
|
if t > -1:
|
||||||
self.short(price, self.invest_pos, True)
|
refs = self.short(price, self.invest_pos, True)
|
||||||
|
if len(refs) > 0:
|
||||||
|
self.write_log(f'卖出委托编号:{refs}')
|
||||||
|
|
||||||
if t > -2:
|
if t == -1 and self.cur_mi_price < price:
|
||||||
self.short(price - self.atr_value * 0.5, self.invest_pos, True)
|
short_price = round_to(price - self.atr_value * 0.5, self.symbol_price_tick)
|
||||||
|
self.write_log(u'发出做空停止单,触发价格为: {}'.format(short_price))
|
||||||
|
refs = self.short(short_price, self.invest_pos, True)
|
||||||
|
if len(refs) > 0:
|
||||||
|
self.write_log(f'卖出委托编号:{refs}')
|
||||||
|
|
||||||
if t > -3:
|
if t == -2 and self.cur_mi_price < price + self.atr_value * 0.5:
|
||||||
self.short(price - self.atr_value, self.invest_pos, True)
|
short_price = round_to(price - self.atr_value, self.symbol_price_tick)
|
||||||
|
self.write_log(u'发出做空停止单,触发价格为: {}'.format(short_price))
|
||||||
|
refs = self.short(short_price, self.invest_pos, True)
|
||||||
|
if len(refs) > 0:
|
||||||
|
self.write_log(f'卖出委托编号:{refs}')
|
||||||
|
|
||||||
if t > -4:
|
if t == -3 and self.cur_mi_price < price + self.atr_value:
|
||||||
self.short(price - self.atr_value * 1.5, self.invest_pos, True)
|
short_price = round_to(price - self.atr_value * 1.5, self.symbol_price_tick)
|
||||||
|
self.write_log(u'发出做空停止单,触发价格为: {}'.format(short_price))
|
||||||
|
refs = self.short(short_price, self.invest_pos, True)
|
||||||
|
if len(refs) > 0:
|
||||||
|
self.write_log(f'卖出委托编号:{refs}')
|
||||||
|
@ -1,40 +1,23 @@
|
|||||||
""""""
|
""""""
|
||||||
import sys
|
import os
|
||||||
|
import uuid
|
||||||
|
import bz2
|
||||||
|
import pickle
|
||||||
|
import copy
|
||||||
|
import traceback
|
||||||
|
|
||||||
from abc import ABC
|
from abc import ABC
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from typing import Any, Callable
|
from typing import Any, Callable
|
||||||
from logging import INFO, ERROR
|
from logging import INFO, ERROR
|
||||||
from vnpy.trader.constant import Interval, Direction, Offset
|
from datetime import datetime
|
||||||
|
from vnpy.trader.constant import Interval, Direction, Offset, Status
|
||||||
from vnpy.trader.object import BarData, TickData, OrderData, TradeData
|
from vnpy.trader.object import BarData, TickData, OrderData, TradeData
|
||||||
from vnpy.trader.utility import virtual
|
from vnpy.trader.utility import virtual, append_data, extract_vt_symbol,get_underlying_symbol
|
||||||
|
|
||||||
from .base import StopOrder, EngineType
|
from .base import StopOrder, EngineType
|
||||||
|
from .cta_grid_trade import CtaGrid, CtaGridTrade
|
||||||
|
from .cta_position import CtaPosition
|
||||||
class CtaComponent(ABC):
|
|
||||||
""" CTA策略基础组件"""
|
|
||||||
def __init__(self, strategy=None, **kwargs):
|
|
||||||
"""
|
|
||||||
构造
|
|
||||||
:param strategy:
|
|
||||||
"""
|
|
||||||
self.strategy = strategy
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
|
||||||
def write_log(self, content: str):
|
|
||||||
"""记录日志"""
|
|
||||||
if self.strategy:
|
|
||||||
self.strategy.write_log(msg=content, level=INFO)
|
|
||||||
else:
|
|
||||||
print(content)
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
|
||||||
def write_error(self, content: str, level: int = ERROR):
|
|
||||||
"""记录错误日志"""
|
|
||||||
if self.strategy:
|
|
||||||
self.strategy.write_log(msg=content, level=level)
|
|
||||||
else:
|
|
||||||
print(content, file=sys.stderr)
|
|
||||||
|
|
||||||
|
|
||||||
class CtaTemplate(ABC):
|
class CtaTemplate(ABC):
|
||||||
@ -56,9 +39,17 @@ class CtaTemplate(ABC):
|
|||||||
self.strategy_name = strategy_name
|
self.strategy_name = strategy_name
|
||||||
self.vt_symbol = vt_symbol
|
self.vt_symbol = vt_symbol
|
||||||
|
|
||||||
self.inited = False
|
self.inited = False # 是否初始化完毕
|
||||||
self.trading = False
|
self.trading = False # 是否开始交易
|
||||||
self.pos = 0
|
self.pos = 0 # 持仓/仓差
|
||||||
|
self.entrust = 0 # 是否正在委托, 0, 无委托 , 1, 委托方向是LONG, -1, 委托方向是SHORT
|
||||||
|
|
||||||
|
self.tick_dict = {} # 记录所有on_tick传入最新tick
|
||||||
|
|
||||||
|
# 保存委托单编号和相关委托单的字典
|
||||||
|
# key为委托单编号
|
||||||
|
# value为该合约相关的委托单
|
||||||
|
self.active_orders = {}
|
||||||
|
|
||||||
# Copy a new variables list here to avoid duplicate insert when multiple
|
# Copy a new variables list here to avoid duplicate insert when multiple
|
||||||
# strategy instances are created with the same strategy class.
|
# strategy instances are created with the same strategy class.
|
||||||
@ -66,8 +57,7 @@ class CtaTemplate(ABC):
|
|||||||
self.variables.insert(0, "inited")
|
self.variables.insert(0, "inited")
|
||||||
self.variables.insert(1, "trading")
|
self.variables.insert(1, "trading")
|
||||||
self.variables.insert(2, "pos")
|
self.variables.insert(2, "pos")
|
||||||
|
self.variables.insert(3, "entrust")
|
||||||
self.update_setting(setting)
|
|
||||||
|
|
||||||
def update_setting(self, setting: dict):
|
def update_setting(self, setting: dict):
|
||||||
"""
|
"""
|
||||||
@ -119,6 +109,23 @@ class CtaTemplate(ABC):
|
|||||||
}
|
}
|
||||||
return strategy_data
|
return strategy_data
|
||||||
|
|
||||||
|
def get_positions(self):
|
||||||
|
""" 返回持仓数量"""
|
||||||
|
pos_list = []
|
||||||
|
if self.pos > 0:
|
||||||
|
pos_list.append({
|
||||||
|
"vt_symbol": self.vt_symbol,
|
||||||
|
"direction": "long",
|
||||||
|
"volume": self.pos
|
||||||
|
})
|
||||||
|
elif self.pos < 0:
|
||||||
|
pos_list.append({
|
||||||
|
"vt_symbol": self.vt_symbol,
|
||||||
|
"direction": "short",
|
||||||
|
"volume": abs(self.pos)
|
||||||
|
})
|
||||||
|
|
||||||
|
@virtual
|
||||||
def on_timer(self):
|
def on_timer(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -178,19 +185,24 @@ class CtaTemplate(ABC):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def buy(self, price: float, volume: float, stop: bool = False, lock: bool = False, vt_symbol: str = ''):
|
def buy(self, price: float, volume: float, stop: bool = False, lock: bool = False,
|
||||||
|
vt_symbol: str = '', order_time: datetime = None, grid: CtaGrid = None):
|
||||||
"""
|
"""
|
||||||
Send buy order to open a long position.
|
Send buy order to open a long position.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.send_order(vt_symbol=vt_symbol,
|
return self.send_order(vt_symbol=vt_symbol,
|
||||||
direction=Direction.LONG,
|
direction=Direction.LONG,
|
||||||
offset=Offset.OPEN,
|
offset=Offset.OPEN,
|
||||||
price=price,
|
price=price,
|
||||||
volume=volume,
|
volume=volume,
|
||||||
stop=stop,
|
stop=stop,
|
||||||
lock=lock)
|
lock=lock,
|
||||||
|
order_time=order_time,
|
||||||
|
grid=grid)
|
||||||
|
|
||||||
def sell(self, price: float, volume: float, stop: bool = False, lock: bool = False, vt_symbol: str = ''):
|
def sell(self, price: float, volume: float, stop: bool = False, lock: bool = False,
|
||||||
|
vt_symbol: str = '', order_time: datetime = None, grid: CtaGrid = None):
|
||||||
"""
|
"""
|
||||||
Send sell order to close a long position.
|
Send sell order to close a long position.
|
||||||
"""
|
"""
|
||||||
@ -200,9 +212,12 @@ class CtaTemplate(ABC):
|
|||||||
price=price,
|
price=price,
|
||||||
volume=volume,
|
volume=volume,
|
||||||
stop=stop,
|
stop=stop,
|
||||||
lock=lock)
|
lock=lock,
|
||||||
|
order_time=order_time,
|
||||||
|
grid=grid)
|
||||||
|
|
||||||
def short(self, price: float, volume: float, stop: bool = False, lock: bool = False, vt_symbol: str = ''):
|
def short(self, price: float, volume: float, stop: bool = False, lock: bool = False,
|
||||||
|
vt_symbol: str = '', order_time: datetime = None, grid: CtaGrid = None):
|
||||||
"""
|
"""
|
||||||
Send short order to open as short position.
|
Send short order to open as short position.
|
||||||
"""
|
"""
|
||||||
@ -212,9 +227,12 @@ class CtaTemplate(ABC):
|
|||||||
price=price,
|
price=price,
|
||||||
volume=volume,
|
volume=volume,
|
||||||
stop=stop,
|
stop=stop,
|
||||||
lock=lock)
|
lock=lock,
|
||||||
|
order_time=order_time,
|
||||||
|
grid=grid)
|
||||||
|
|
||||||
def cover(self, price: float, volume: float, stop: bool = False, lock: bool = False, vt_symbol: str = ''):
|
def cover(self, price: float, volume: float, stop: bool = False, lock: bool = False,
|
||||||
|
vt_symbol: str = '', order_time: datetime = None, grid: CtaGrid = None):
|
||||||
"""
|
"""
|
||||||
Send cover order to close a short position.
|
Send cover order to close a short position.
|
||||||
"""
|
"""
|
||||||
@ -224,7 +242,9 @@ class CtaTemplate(ABC):
|
|||||||
price=price,
|
price=price,
|
||||||
volume=volume,
|
volume=volume,
|
||||||
stop=stop,
|
stop=stop,
|
||||||
lock=lock)
|
lock=lock,
|
||||||
|
order_time=order_time,
|
||||||
|
grid=grid)
|
||||||
|
|
||||||
def send_order(
|
def send_order(
|
||||||
self,
|
self,
|
||||||
@ -234,7 +254,9 @@ class CtaTemplate(ABC):
|
|||||||
price: float,
|
price: float,
|
||||||
volume: float,
|
volume: float,
|
||||||
stop: bool = False,
|
stop: bool = False,
|
||||||
lock: bool = False
|
lock: bool = False,
|
||||||
|
order_time: datetime = None,
|
||||||
|
grid: CtaGrid = None
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Send a new order.
|
Send a new order.
|
||||||
@ -243,20 +265,44 @@ class CtaTemplate(ABC):
|
|||||||
if vt_symbol == '':
|
if vt_symbol == '':
|
||||||
vt_symbol = self.vt_symbol
|
vt_symbol = self.vt_symbol
|
||||||
|
|
||||||
if self.trading:
|
if not self.trading:
|
||||||
|
return []
|
||||||
vt_orderids = self.cta_engine.send_order(
|
vt_orderids = self.cta_engine.send_order(
|
||||||
self, vt_symbol, direction, offset, price, volume, stop, lock
|
self, vt_symbol, direction, offset, price, volume, stop, lock
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if order_time is None:
|
||||||
|
order_time = datetime.now()
|
||||||
|
|
||||||
|
for vt_orderid in vt_orderids:
|
||||||
|
d = {
|
||||||
|
'direction': direction.value,
|
||||||
|
'offset': offset.value,
|
||||||
|
'vt_symbol': vt_symbol,
|
||||||
|
'price': price,
|
||||||
|
'volume': volume,
|
||||||
|
'traded': 0,
|
||||||
|
'order_time': order_time,
|
||||||
|
'status': Status.SUBMITTING
|
||||||
|
}
|
||||||
|
if grid:
|
||||||
|
d.update({'grid': grid})
|
||||||
|
grid.order_ids.append(vt_orderid)
|
||||||
|
self.active_orders.update({vt_orderid: d})
|
||||||
|
if direction == Direction.LONG:
|
||||||
|
self.entrust = 1
|
||||||
|
elif direction == Direction.SHORT:
|
||||||
|
self.entrust = -1
|
||||||
return vt_orderids
|
return vt_orderids
|
||||||
else:
|
|
||||||
return []
|
|
||||||
|
|
||||||
def cancel_order(self, vt_orderid: str):
|
def cancel_order(self, vt_orderid: str):
|
||||||
"""
|
"""
|
||||||
Cancel an existing order.
|
Cancel an existing order.
|
||||||
"""
|
"""
|
||||||
if self.trading:
|
if self.trading:
|
||||||
self.cta_engine.cancel_order(self, vt_orderid)
|
return self.cta_engine.cancel_order(self, vt_orderid)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def cancel_all(self):
|
def cancel_all(self):
|
||||||
"""
|
"""
|
||||||
@ -265,12 +311,32 @@ class CtaTemplate(ABC):
|
|||||||
if self.trading:
|
if self.trading:
|
||||||
self.cta_engine.cancel_all(self)
|
self.cta_engine.cancel_all(self)
|
||||||
|
|
||||||
|
def is_upper_limit(self, symbol):
|
||||||
|
"""是否涨停"""
|
||||||
|
tick = self.tick_dict.get(symbol, None)
|
||||||
|
if tick is None or tick.limit_up is None or tick.limit_up == 0:
|
||||||
|
return False
|
||||||
|
if tick.bid_price_1 == tick.limit_up:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def is_lower_limit(self, symbol):
|
||||||
|
"""是否跌停"""
|
||||||
|
tick = self.tick_dict.get(symbol, None)
|
||||||
|
if tick is None or tick.limit_down is None or tick.limit_down == 0:
|
||||||
|
return False
|
||||||
|
if tick.ask_price_1 == tick.limit_down:
|
||||||
|
return True
|
||||||
|
|
||||||
def write_log(self, msg: str, level: int = INFO):
|
def write_log(self, msg: str, level: int = INFO):
|
||||||
"""
|
"""
|
||||||
Write a log message.
|
Write a log message.
|
||||||
"""
|
"""
|
||||||
self.cta_engine.write_log(msg=msg, strategy_name=self.strategy_name, level=level)
|
self.cta_engine.write_log(msg=msg, strategy_name=self.strategy_name, level=level)
|
||||||
|
|
||||||
|
def write_error(self, msg: str):
|
||||||
|
"""write error log message"""
|
||||||
|
self.write_log(msg=msg, level=ERROR)
|
||||||
|
|
||||||
def get_engine_type(self):
|
def get_engine_type(self):
|
||||||
"""
|
"""
|
||||||
Return whether the cta_engine is backtesting or live trading.
|
Return whether the cta_engine is backtesting or live trading.
|
||||||
@ -452,3 +518,474 @@ class TargetPosTemplate(CtaTemplate):
|
|||||||
else:
|
else:
|
||||||
vt_orderids = self.short(short_price, abs(pos_change))
|
vt_orderids = self.short(short_price, abs(pos_change))
|
||||||
self.vt_orderids.extend(vt_orderids)
|
self.vt_orderids.extend(vt_orderids)
|
||||||
|
|
||||||
|
|
||||||
|
class CtaProTemplate(CtaTemplate):
|
||||||
|
"""
|
||||||
|
增强模板
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 逻辑过程日志
|
||||||
|
dist_fieldnames = ['datetime', 'symbol', 'volume', 'price',
|
||||||
|
'operation', 'signal', 'stop_price', 'target_price',
|
||||||
|
'long_pos', 'short_pos']
|
||||||
|
|
||||||
|
def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
|
||||||
|
""""""
|
||||||
|
super(CtaProTemplate, self).__init__(
|
||||||
|
cta_engine, strategy_name, vt_symbol, setting
|
||||||
|
)
|
||||||
|
|
||||||
|
self.idx_symbol = None # 指数合约
|
||||||
|
|
||||||
|
self.price_tick = 1 # 商品的最小价格跳动
|
||||||
|
self.symbol_size = 10 # 商品得合约乘数
|
||||||
|
|
||||||
|
self.cur_datetime = None # 当前Tick时间
|
||||||
|
|
||||||
|
self.cur_mi_tick = None # 最新的主力合约tick( vt_symbol)
|
||||||
|
self.cur_99_tick = None # 最新得指数合约tick( idx_symbol)
|
||||||
|
|
||||||
|
self.cur_mi_price = None # 当前价(主力合约 vt_symbol)
|
||||||
|
self.cur_99_price = None # 当前价(tick时,根据tick更新,onBar回测时,根据bar.close更新)
|
||||||
|
|
||||||
|
self.cancel_seconds = 120 # 撤单时间(秒)
|
||||||
|
|
||||||
|
self.backtesting = False
|
||||||
|
|
||||||
|
self.klines = {} # K线字典: kline_name: kline
|
||||||
|
|
||||||
|
# 增加仓位管理模块
|
||||||
|
self.position = CtaPosition(strategy=self)
|
||||||
|
|
||||||
|
# 增加网格持久化模块
|
||||||
|
self.gt = CtaGridTrade(strategy=self)
|
||||||
|
|
||||||
|
# 增加指数合约
|
||||||
|
if 'idx_symbol' not in self.parameters:
|
||||||
|
self.parameters.append('idx_symbol')
|
||||||
|
|
||||||
|
if 'backtesting' not in self.parameters:
|
||||||
|
self.parameters.append('backtesting')
|
||||||
|
|
||||||
|
def update_setting(self, setting: dict):
|
||||||
|
"""
|
||||||
|
Update strategy parameter wtih value in setting dict.
|
||||||
|
"""
|
||||||
|
for name in self.parameters:
|
||||||
|
if name in setting:
|
||||||
|
setattr(self, name, setting[name])
|
||||||
|
|
||||||
|
if self.idx_symbol is None:
|
||||||
|
symbol, exchange = extract_vt_symbol(self.vt_symbol)
|
||||||
|
self.idx_symbol = get_underlying_symbol(symbol).upper() + '99.' + exchange.value
|
||||||
|
if self.vt_symbol != self.idx_symbol:
|
||||||
|
self.write_log(f'指数合约:{self.idx_symbol}, 主力合约:{self.vt_symbol}')
|
||||||
|
self.price_tick = self.cta_engine.get_price_tick(self.vt_symbol)
|
||||||
|
self.symbol_size = self.cta_engine.get_size(self.vt_symbol)
|
||||||
|
|
||||||
|
def save_klines_to_cache(self, kline_names: list = []):
|
||||||
|
"""
|
||||||
|
保存K线数据到缓存
|
||||||
|
:param kline_names: 一般为self.klines的keys
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if len(kline_names) == 0:
|
||||||
|
kline_names = list(self.klines.keys())
|
||||||
|
|
||||||
|
# 获取保存路径
|
||||||
|
save_path = self.cta_engine.get_data_path()
|
||||||
|
# 保存缓存的文件名
|
||||||
|
file_name = os.path.abspath(os.path.join(save_path, f'{self.strategy_name}_klines.pkb2'))
|
||||||
|
with bz2.BZ2File(file_name, 'wb') as f:
|
||||||
|
klines = {}
|
||||||
|
for kline_name in kline_names:
|
||||||
|
klines.update({kline_name: self.klines.get(kline_name, None)})
|
||||||
|
pickle.dump(klines, f)
|
||||||
|
|
||||||
|
def load_klines_from_cache(self, kline_names: list = []):
|
||||||
|
"""
|
||||||
|
从缓存加载K线数据
|
||||||
|
:param kline_names:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if len(kline_names) == 0:
|
||||||
|
kline_names = list(self.klines.keys())
|
||||||
|
|
||||||
|
save_path = self.cta_engine.get_data_path()
|
||||||
|
file_name = os.path.abspath(os.path.join(save_path, f'{self.strategy_name}_klines.pkb2'))
|
||||||
|
try:
|
||||||
|
last_bar_dt = None
|
||||||
|
with bz2.BZ2File(file_name, 'rb') as f:
|
||||||
|
klines = pickle.load(f)
|
||||||
|
# 逐一恢复K线
|
||||||
|
for kline_name in kline_names:
|
||||||
|
# 缓存的k线实例
|
||||||
|
cache_kline = klines.get(kline_name, None)
|
||||||
|
# 当前策略实例的K线实例
|
||||||
|
strategy_kline = self.klines.get(kline_name, None)
|
||||||
|
|
||||||
|
if cache_kline and strategy_kline:
|
||||||
|
# 临时保存当前的回调函数
|
||||||
|
cb_on_bar = strategy_kline.cb_on_bar
|
||||||
|
# 缓存实例数据 =》 当前实例数据
|
||||||
|
strategy_kline.__dict__.update(cache_kline.__dict__)
|
||||||
|
|
||||||
|
# 所有K线的最后时间
|
||||||
|
if last_bar_dt and strategy_kline.cur_datetime:
|
||||||
|
last_bar_dt = max(last_bar_dt, strategy_kline.cur_datetime)
|
||||||
|
else:
|
||||||
|
last_bar_dt = strategy_kline.cur_datetime
|
||||||
|
|
||||||
|
# 重新绑定k线策略与on_bar回调函数
|
||||||
|
strategy_kline.strategy = self
|
||||||
|
strategy_kline.cb_on_bar = cb_on_bar
|
||||||
|
|
||||||
|
self.write_log(f'恢复{kline_name}缓存数据,最新bar结束时间:{last_bar_dt}')
|
||||||
|
|
||||||
|
self.write_log(u'加载缓存k线数据完毕')
|
||||||
|
return last_bar_dt
|
||||||
|
except Exception as ex:
|
||||||
|
self.write_error(f'加载缓存K线数据失败:{str(ex)}')
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_klines_snapshot(self):
|
||||||
|
"""返回当前klines的切片数据"""
|
||||||
|
try:
|
||||||
|
d = {
|
||||||
|
'strategy': self.strategy_name,
|
||||||
|
'datetime': datetime.now()}
|
||||||
|
|
||||||
|
for kline_name in self.klines.keys():
|
||||||
|
d.update({kline_name: self.klines.get(kline_name).get_data()})
|
||||||
|
return d
|
||||||
|
except Exception as ex:
|
||||||
|
self.write_error(u'获取klines切片数据失败')
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def init_position(self):
|
||||||
|
"""
|
||||||
|
初始化Positin
|
||||||
|
使用网格的持久化,获取开仓状态的多空单,更新
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.write_log(u'init_position(),初始化持仓')
|
||||||
|
pos_symbols = set()
|
||||||
|
if len(self.gt.up_grids) <= 0:
|
||||||
|
self.position.short_pos = 0
|
||||||
|
# 加载已开仓的空单数据,网格JSON
|
||||||
|
short_grids = self.gt.load(direction=Direction.SHORT, open_status_filter=[True])
|
||||||
|
if len(short_grids) == 0:
|
||||||
|
self.write_log(u'没有持久化的空单数据')
|
||||||
|
self.gt.up_grids = []
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.gt.up_grids = short_grids
|
||||||
|
for sg in short_grids:
|
||||||
|
if len(sg.order_ids) > 0 or sg.order_status:
|
||||||
|
self.write_log(f'重置委托状态:{sg.order_status},清除委托单:{sg.order_ids}')
|
||||||
|
sg.order_status = False
|
||||||
|
sg.order_ids = []
|
||||||
|
|
||||||
|
short_symbol = sg.snapshot.get('mi_symbol', self.vt_symbol)
|
||||||
|
pos_symbols.add(short_symbol)
|
||||||
|
self.write_log(u'加载持仓空单[{},价格:{}],[指数:{},价格:{}],数量:{}手'
|
||||||
|
.format(short_symbol, sg.snapshot.get('open_price'),
|
||||||
|
self.idx_symbol, sg.open_price, sg.volume))
|
||||||
|
self.position.short_pos -= sg.volume
|
||||||
|
|
||||||
|
self.write_log(u'持久化空单,共持仓:{}手'.format(abs(self.position.short_pos)))
|
||||||
|
|
||||||
|
if len(self.gt.dn_grids) <= 0:
|
||||||
|
# 加载已开仓的多数据,网格JSON
|
||||||
|
self.position.long_pos = 0
|
||||||
|
long_grids = self.gt.load(direction=Direction.LONG, open_status_filter=[True])
|
||||||
|
if len(long_grids) == 0:
|
||||||
|
self.write_log(u'没有持久化的多单数据')
|
||||||
|
self.gt.dn_grids = []
|
||||||
|
else:
|
||||||
|
self.gt.dn_grids = long_grids
|
||||||
|
for lg in long_grids:
|
||||||
|
|
||||||
|
if len(lg.order_ids) > 0 or lg.order_status:
|
||||||
|
self.write_log(f'重置委托状态:{lg.order_status},清除委托单:{lg.order_ids}')
|
||||||
|
lg.order_status = False
|
||||||
|
lg.order_ids = []
|
||||||
|
# lg.type = self.line.name
|
||||||
|
long_symbol = lg.snapshot.get('mi_symbol', self.vt_symbol)
|
||||||
|
pos_symbols.add(long_symbol)
|
||||||
|
|
||||||
|
self.write_log(u'加载持仓多单[{},价格:{}],[指数{},价格:{}],数量:{}手'
|
||||||
|
.format(lg.snapshot.get('miSymbol'), lg.snapshot.get('open_price'),
|
||||||
|
self.idx_symbol, lg.open_price, lg.volume))
|
||||||
|
self.position.long_pos += lg.volume
|
||||||
|
|
||||||
|
self.write_log(f'持久化多单,共持仓:{self.position.long_pos}手')
|
||||||
|
|
||||||
|
self.position.pos = self.position.long_pos + self.position.short_pos
|
||||||
|
|
||||||
|
self.write_log(
|
||||||
|
u'{}加载持久化数据完成,多单:{},空单:{},共:{}手'
|
||||||
|
.format(self.strategy_name,
|
||||||
|
self.position.long_pos,
|
||||||
|
abs(self.position.short_pos),
|
||||||
|
self.position.pos))
|
||||||
|
self.pos = self.position.pos
|
||||||
|
self.gt.save()
|
||||||
|
self.display_grids()
|
||||||
|
|
||||||
|
if not self.backtesting:
|
||||||
|
pos_symbols.add(self.vt_symbol)
|
||||||
|
pos_symbols.add(self.idx_symbol)
|
||||||
|
# 如果持仓的合约,不在self.vt_symbol中,需要订阅
|
||||||
|
for symbol in list(pos_symbols):
|
||||||
|
self.write_log(f'新增订阅合约:{symbol}')
|
||||||
|
self.cta_engine.subscribe_symbol(strategy_name=self.strategy_name, vt_symbol=symbol)
|
||||||
|
|
||||||
|
def get_positions(self):
|
||||||
|
"""
|
||||||
|
获取策略当前持仓(重构,使用主力合约)
|
||||||
|
:return: [{'vt_symbol':symbol,'direction':direction,'volume':volume]
|
||||||
|
"""
|
||||||
|
if not self.position:
|
||||||
|
return []
|
||||||
|
pos_list = []
|
||||||
|
|
||||||
|
if self.position.long_pos > 0:
|
||||||
|
for g in self.gt.get_opened_grids(direction=Direction.LONG):
|
||||||
|
vt_symbol = g.snapshot.get('mi_symbol', self.vt_symbol)
|
||||||
|
open_price = g.snapshot.get('open_price', g.openPrice)
|
||||||
|
pos_list.append({'vt_symbol': vt_symbol,
|
||||||
|
'direction': 'long',
|
||||||
|
'volume': g.volume - g.traded_volume,
|
||||||
|
'price': open_price})
|
||||||
|
|
||||||
|
if abs(self.position.short_pos) > 0:
|
||||||
|
for g in self.gt.get_opened_grids(direction=Direction.SHORT):
|
||||||
|
vt_symbol = g.snapshot.get('mi_symbol', self.vt_symbol)
|
||||||
|
open_price = g.snapshot.get('open_price', g.open_price)
|
||||||
|
pos_list.append({'vt_symbol': vt_symbol,
|
||||||
|
'direction': 'short',
|
||||||
|
'volume': abs(g.volume - g.traded_volume),
|
||||||
|
'price': open_price})
|
||||||
|
|
||||||
|
if self.cur_datetime and (datetime.now() - self.cur_datetime).total_seconds() < 10:
|
||||||
|
self.write_log(u'当前持仓:{}'.format(pos_list))
|
||||||
|
return pos_list
|
||||||
|
|
||||||
|
def tns_cancel_logic(self, dt, force=False):
|
||||||
|
"撤单逻辑"""
|
||||||
|
if len(self.active_orders) < 1:
|
||||||
|
self.entrust = 0
|
||||||
|
return
|
||||||
|
|
||||||
|
for vt_orderid in list(self.active_orders.keys()):
|
||||||
|
order_info = self.active_orders.get(vt_orderid)
|
||||||
|
if order_info.get('status', None) in [Status.CANCELLED, Status.REJECTED]:
|
||||||
|
self.active_orders.pop(vt_orderid, None)
|
||||||
|
continue
|
||||||
|
|
||||||
|
order_time = order_info.get('order_time')
|
||||||
|
over_ms = (dt - order_time).total_seconds()
|
||||||
|
if (over_ms > self.cancel_seconds) \
|
||||||
|
or force: # 超过设置的时间还未成交
|
||||||
|
self.write_log(f'{dt}, 超时{over_ms}秒未成交,取消委托单:{order_info}')
|
||||||
|
|
||||||
|
if self.cancel_order(vt_orderid):
|
||||||
|
order_info.update({'status': Status.CANCELLING})
|
||||||
|
else:
|
||||||
|
order_info.update({'status': Status.CANCELLED})
|
||||||
|
|
||||||
|
if len(self.active_orders) < 1:
|
||||||
|
self.entrust = 0
|
||||||
|
|
||||||
|
def tns_switch_long_pos(self):
|
||||||
|
"""切换合约,从持仓的非主力合约,切换至主力合约"""
|
||||||
|
|
||||||
|
if self.entrust != 0 and self.position.long_pos == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.cur_mi_price == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
none_mi_grid = None
|
||||||
|
none_mi_symbol = None
|
||||||
|
|
||||||
|
# 找出非主力合约的持仓网格
|
||||||
|
for g in self.gt.get_opened_grids(direction=Direction.LONG):
|
||||||
|
none_mi_symbol = g.snapshot.get('mi_symbol')
|
||||||
|
if none_mi_symbol is None or none_mi_symbol == self.vt_symbol:
|
||||||
|
# 如果持仓的合约,跟策略配置的vt_symbol一致,则不处理
|
||||||
|
continue
|
||||||
|
if not g.open_status or g.order_status or g.volume - g.traded_volume <= 0:
|
||||||
|
continue
|
||||||
|
none_mi_grid = g
|
||||||
|
if g.traded_volume > 0 and g.volume - g.traded_volume > 0:
|
||||||
|
g.volume -= g.traded_volume
|
||||||
|
g.traded_volume = 0
|
||||||
|
break
|
||||||
|
if none_mi_grid is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 找到行情中非主力合约/主力合约的最新价
|
||||||
|
none_mi_tick = self.tick_dict.get(none_mi_symbol)
|
||||||
|
mi_tick = self.tick_dict.get(self.vt_symbol, None)
|
||||||
|
if none_mi_tick is None or mi_tick is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 如果涨停价,不做卖出
|
||||||
|
if self.is_upper_limit(none_mi_symbol) or self.is_upper_limit(self.vt_symbol):
|
||||||
|
return
|
||||||
|
none_mi_price = max(none_mi_tick.last_price, none_mi_tick.bid_price_1)
|
||||||
|
|
||||||
|
grid = copy.copy(none_mi_grid)
|
||||||
|
|
||||||
|
# 委托卖出非主力合约
|
||||||
|
order_ids = self.sell(price=none_mi_price, volume=none_mi_grid.volume, vt_symbol=none_mi_symbol,
|
||||||
|
grid=none_mi_grid)
|
||||||
|
if len(order_ids) > 0:
|
||||||
|
self.write_log(f'切换合约,委托卖出非主力合约{none_mi_symbol}持仓:{none_mi_grid.volume}')
|
||||||
|
|
||||||
|
# 添加买入主力合约
|
||||||
|
grid.id = str(uuid.uuid1())
|
||||||
|
grid.snapshot.update({'mi_symbol': self.vt_symbol, 'open_price': self.cur_mi_price})
|
||||||
|
self.gt.dn_grids.append(grid)
|
||||||
|
|
||||||
|
order_ids = self.buy(price=self.cur_mi_price, volume=grid.volume, vt_symbol=self.vt_symbol, grid=grid)
|
||||||
|
if len(order_ids) > 0:
|
||||||
|
self.write_log(u'切换合约,委托买入主力合约:{},价格:{},数量:{}'
|
||||||
|
.format(self.vt_symbol, self.cur_mi_price, grid.volume))
|
||||||
|
self.gt.save()
|
||||||
|
else:
|
||||||
|
self.write_error(f'委托买入主力合约:{self.vt_symbol}失败')
|
||||||
|
else:
|
||||||
|
self.write_error(f'委托卖出非主力合约:{none_mi_symbol}失败')
|
||||||
|
|
||||||
|
def tns_switch_short_pos(self):
|
||||||
|
"""切换合约,从持仓的非主力合约,切换至主力合约"""
|
||||||
|
if self.entrust != 0 and self.position.short_pos == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.cur_mi_price == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
none_mi_grid = None
|
||||||
|
none_mi_symbol = None
|
||||||
|
|
||||||
|
# 找出非主力合约的持仓网格
|
||||||
|
for g in self.gt.get_opened_grids(direction=Direction.SHORT):
|
||||||
|
none_mi_symbol = g.snapshot.get('miSymbol')
|
||||||
|
if none_mi_symbol is None or none_mi_symbol == self.vt_symbol:
|
||||||
|
continue
|
||||||
|
if not g.open_status or g.order_status or g.volume - g.traded_volume <= 0:
|
||||||
|
continue
|
||||||
|
none_mi_grid = g
|
||||||
|
if g.traded_volume > 0 and g.volume - g.traded_volume > 0:
|
||||||
|
g.volume -= g.traded_volume
|
||||||
|
g.traded_volume = 0
|
||||||
|
break
|
||||||
|
|
||||||
|
# 找不到与主力合约不一致的持仓网格
|
||||||
|
if none_mi_grid is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 找到行情中非主力合约的最新价
|
||||||
|
none_mi_tick = self.tick_dict.get(none_mi_symbol)
|
||||||
|
mi_tick = self.tick_dict.get(self.vt_symbol, None)
|
||||||
|
if none_mi_tick is None or mi_tick is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 如果跌停价,不做cover
|
||||||
|
if self.is_lower_limit(none_mi_symbol) or self.is_lower_limit(self.vt_symbol):
|
||||||
|
return
|
||||||
|
none_mi_price = max(none_mi_tick.last_price, none_mi_tick.bid_price_1)
|
||||||
|
|
||||||
|
grid = copy.copy(none_mi_grid)
|
||||||
|
# 委托平空非主力合约
|
||||||
|
order_ids = self.cover(price=none_mi_price, volume=none_mi_grid.volume, vt_symbol=self.vt_symbol,
|
||||||
|
grid=none_mi_grid)
|
||||||
|
if len(order_ids) > 0:
|
||||||
|
self.write_log(f'委托平空非主力合约{none_mi_symbol}持仓:{none_mi_grid.volume}')
|
||||||
|
|
||||||
|
# 添加卖出主力合约
|
||||||
|
grid.id = str(uuid.uuid1())
|
||||||
|
grid.snapshot.update({'mi_symbol': self.vt_symbol, 'open_price': self.cur_mi_price})
|
||||||
|
self.gt.up_grids.append(grid)
|
||||||
|
order_ids = self.short(price=self.cur_mi_price, volume=grid.volume, vt_symbol=self.vt_symbol, grid=grid)
|
||||||
|
if len(order_ids) > 0:
|
||||||
|
self.write_log(f'委托做空主力合约:{self.vt_symbol},价格:{self.cur_mi_price},数量:{grid.volume}')
|
||||||
|
self.gt.save()
|
||||||
|
else:
|
||||||
|
self.write_error(f'委托做空主力合约:{self.vt_symbol}失败')
|
||||||
|
else:
|
||||||
|
self.write_error(f'委托平空非主力合约:{none_mi_symbol}失败')
|
||||||
|
|
||||||
|
def display_grids(self):
|
||||||
|
"""更新网格显示信息"""
|
||||||
|
if not self.inited:
|
||||||
|
return
|
||||||
|
|
||||||
|
up_grids_info = self.gt.to_str(direction=Direction.SHORT)
|
||||||
|
if len(self.gt.up_grids) > 0:
|
||||||
|
self.write_log(up_grids_info)
|
||||||
|
|
||||||
|
dn_grids_info = self.gt.to_str(direction=Direction.LONG)
|
||||||
|
if len(self.gt.dn_grids) > 0:
|
||||||
|
self.write_log(dn_grids_info)
|
||||||
|
|
||||||
|
def display_tns(self):
|
||||||
|
"""显示事务的过程记录=》 log"""
|
||||||
|
if not self.inited:
|
||||||
|
return
|
||||||
|
self.write_log(u'{} 当前指数{}价格:{},当前主力{}价格:{}'
|
||||||
|
.format(self.cur_datetime,
|
||||||
|
self.idx_symbol, self.cur_99_price,
|
||||||
|
self.vt_symbol, self.cur_mi_price))
|
||||||
|
if hasattr(self, 'policy'):
|
||||||
|
policy = getattr(self, 'policy')
|
||||||
|
op = getattr(policy, 'to_json', None)
|
||||||
|
if callable(op):
|
||||||
|
self.write_log(u'当前Policy:{}'.format(policy.to_json()))
|
||||||
|
|
||||||
|
def save_dist(self, dist_data):
|
||||||
|
"""
|
||||||
|
保存策略逻辑过程记录=》 csv文件按
|
||||||
|
:param dist_data:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if self.backtesting:
|
||||||
|
save_path = self.cta_engine.get_logs_path()
|
||||||
|
else:
|
||||||
|
save_path = self.cta_engine.get_data_path()
|
||||||
|
try:
|
||||||
|
if self.position:
|
||||||
|
dist_data.update({'long_pos': self.position.long_pos})
|
||||||
|
dist_data.update({'short_pos': self.position.short_pos})
|
||||||
|
|
||||||
|
file_name = os.path.abspath(os.path.join(save_path, f'{self.strategy_name}_dist.csv'))
|
||||||
|
append_data(file_name=file_name, dict_data=dist_data, field_names=self.dist_fieldnames)
|
||||||
|
except Exception as ex:
|
||||||
|
self.write_error(u'save_dist 异常:{} {}'.format(str(ex), traceback.format_exc()))
|
||||||
|
|
||||||
|
def save_tns(self, tns_data):
|
||||||
|
"""
|
||||||
|
保存多空事务记录=》csv文件,便于后续分析
|
||||||
|
:param tns_data:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if self.backtesting:
|
||||||
|
save_path = self.cta_engine.get_logs_path()
|
||||||
|
else:
|
||||||
|
save_path = self.cta_engine.get_data_path()
|
||||||
|
|
||||||
|
try:
|
||||||
|
file_name = os.path.abspath(os.path.join(save_path, f'{self.strategy_name}_tns.csv'))
|
||||||
|
append_data(file_name=file_name, dict_data=tns_data)
|
||||||
|
except Exception as ex:
|
||||||
|
self.write_error(u'save_tns 异常:{} {}'.format(str(ex), traceback.format_exc()))
|
||||||
|
|
||||||
|
def send_wechat(self, msg: str):
|
||||||
|
"""实盘时才发送微信"""
|
||||||
|
if self.backtesting:
|
||||||
|
return
|
||||||
|
self.cta_engine.send_wechat(msg=msg, strategy=self)
|
||||||
|
65
vnpy/app/cta_strategy_pro/test_line_bar_01.py
Normal file
65
vnpy/app/cta_strategy_pro/test_line_bar_01.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# flake8: noqa
|
||||||
|
|
||||||
|
# 测试 app.cta_strategy_pro.CtaLineBar组件
|
||||||
|
# 从通达信获取历史交易记录,模拟tick。推送至line_bar
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
|
||||||
|
vnpy_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
||||||
|
if vnpy_root not in sys.path:
|
||||||
|
print(f'sys.path apppend:{vnpy_root}')
|
||||||
|
sys.path.append(vnpy_root)
|
||||||
|
|
||||||
|
os.environ["VNPY_TESTING"] = "1"
|
||||||
|
|
||||||
|
from vnpy.trader.constant import Interval, Exchange
|
||||||
|
from vnpy.data.tdx.tdx_common import FakeStrategy
|
||||||
|
from vnpy.data.tdx.tdx_future_data import TdxFutureData
|
||||||
|
from vnpy.app.cta_strategy_pro.cta_line_bar import CtaLineBar
|
||||||
|
from vnpy.trader.object import TickData
|
||||||
|
from vnpy.trader.utility import get_trading_date
|
||||||
|
|
||||||
|
t1 = FakeStrategy()
|
||||||
|
|
||||||
|
tdx_api = TdxFutureData(strategy=t1)
|
||||||
|
|
||||||
|
|
||||||
|
def on_bar(bar):
|
||||||
|
print(f'{bar.__dict__}')
|
||||||
|
|
||||||
|
|
||||||
|
# 创建10秒周期的k线
|
||||||
|
kline_setting = {}
|
||||||
|
kline_setting["name"] = "S10"
|
||||||
|
kline_setting['interval'] = Interval.SECOND
|
||||||
|
kline_setting['bar_interval'] = 10
|
||||||
|
kline_setting['price_tick'] = 0.5
|
||||||
|
kline_setting['underlying_symbol'] = 'J'
|
||||||
|
kline_s10 = CtaLineBar(strategy=t1, cb_on_bar=on_bar, setting=kline_setting)
|
||||||
|
|
||||||
|
ret, result = tdx_api.get_history_transaction_data('J99', '20200106')
|
||||||
|
# for data in result[0:10] + result[-10:]:
|
||||||
|
# print(data)
|
||||||
|
|
||||||
|
for data in result:
|
||||||
|
dt = data['datetime']
|
||||||
|
price = float(data['price'])
|
||||||
|
volume = float(data['volume'])
|
||||||
|
|
||||||
|
tick = TickData(
|
||||||
|
gateway_name='tdx',
|
||||||
|
datetime=dt,
|
||||||
|
last_price=price,
|
||||||
|
volume=volume,
|
||||||
|
symbol='J99',
|
||||||
|
exchange=Exchange('DCE'),
|
||||||
|
date=dt.strftime('%Y-%m-%d'),
|
||||||
|
time=dt.strftime('%H:%M:%S'),
|
||||||
|
trading_day=get_trading_date(dt)
|
||||||
|
)
|
||||||
|
|
||||||
|
kline_s10.on_tick(tick)
|
||||||
|
|
||||||
|
os._exit(0)
|
442
vnpy/app/cta_strategy_pro/test_line_bar_02.py
Normal file
442
vnpy/app/cta_strategy_pro/test_line_bar_02.py
Normal file
@ -0,0 +1,442 @@
|
|||||||
|
# flake8: noqa
|
||||||
|
|
||||||
|
# 测试 app.cta_strategy_pro.CtaLineBar组件
|
||||||
|
# 从通达信获取历史交易记录,模拟tick。推送至line_bar
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import traceback
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
vnpy_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
||||||
|
if vnpy_root not in sys.path:
|
||||||
|
print(f'sys.path apppend:{vnpy_root}')
|
||||||
|
sys.path.append(vnpy_root)
|
||||||
|
|
||||||
|
os.environ["VNPY_TESTING"] = "1"
|
||||||
|
|
||||||
|
from vnpy.trader.constant import Interval, Exchange
|
||||||
|
from vnpy.trader.object import BarData
|
||||||
|
from vnpy.app.cta_strategy_pro.cta_line_bar import (
|
||||||
|
CtaLineBar,
|
||||||
|
CtaMinuteBar,
|
||||||
|
CtaHourBar,
|
||||||
|
CtaDayBar,
|
||||||
|
CtaWeekBar)
|
||||||
|
from vnpy.trader.utility import round_to
|
||||||
|
|
||||||
|
class test_strategy(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
self.price_tick = 1
|
||||||
|
self.underlying_symbol = 'I'
|
||||||
|
self.vt_symbol = 'I99'
|
||||||
|
|
||||||
|
self.lineM5 = None
|
||||||
|
self.lineM30 = None
|
||||||
|
self.lineH1 = None
|
||||||
|
self.lineH2 = None
|
||||||
|
self.lineD = None
|
||||||
|
self.lineW = None
|
||||||
|
|
||||||
|
self.TMinuteInterval = 1
|
||||||
|
|
||||||
|
self.save_m30_bars = []
|
||||||
|
self.save_h1_bars = []
|
||||||
|
self.save_h2_bars = []
|
||||||
|
self.save_d_bars = []
|
||||||
|
|
||||||
|
self.save_w_bars = []
|
||||||
|
|
||||||
|
def createM5(self):
|
||||||
|
"""使用ctalinbar,创建5分钟K线"""
|
||||||
|
lineM5Setting = {}
|
||||||
|
lineM5Setting['name'] = u'M5'
|
||||||
|
lineM5Setting['interval'] = Interval.MINUTE
|
||||||
|
lineM5Setting['bar_interval'] = 5
|
||||||
|
lineM5Setting['mode'] = CtaLineBar.TICK_MODE
|
||||||
|
lineM5Setting['price_tick'] = self.price_tick
|
||||||
|
lineM5Setting['underlying_symbol'] = self.underlying_symbol
|
||||||
|
self.lineM5 = CtaLineBar(self, self.onBarM5, lineM5Setting)
|
||||||
|
|
||||||
|
def onBarM5(self, bar):
|
||||||
|
self.write_log(self.lineM5.get_last_bar_str())
|
||||||
|
|
||||||
|
def createlineM30_with_macd(self):
|
||||||
|
"""使用CtaLineBar,创建30分钟时间"""
|
||||||
|
# 创建M30 K线
|
||||||
|
lineM30Setting = {}
|
||||||
|
lineM30Setting['name'] = u'M30'
|
||||||
|
lineM30Setting['interval'] = Interval.MINUTE
|
||||||
|
lineM30Setting['bar_interval'] = 30
|
||||||
|
lineM30Setting['para_macd_fast_len'] = 26
|
||||||
|
lineM30Setting['para_macd_slow_len'] = 12
|
||||||
|
lineM30Setting['para_macd_signal_len'] = 9
|
||||||
|
lineM30Setting['price_tick'] = self.price_tick
|
||||||
|
lineM30Setting['underlying_symbol'] = self.underlying_symbol
|
||||||
|
self.lineM30 = CtaLineBar(self, self.onBarM30MACD, lineM30Setting)
|
||||||
|
|
||||||
|
def onBarM30MACD(self, bar):
|
||||||
|
self.write_log(self.lineM30.get_last_bar_str())
|
||||||
|
|
||||||
|
def createLineM30(self):
|
||||||
|
"""使用ctaMinuteBar, 测试内部自动写入csv文件"""
|
||||||
|
# 创建M30 K线
|
||||||
|
lineM30Setting = {}
|
||||||
|
lineM30Setting['name'] = u'M30'
|
||||||
|
lineM30Setting['interval'] = Interval.MINUTE
|
||||||
|
lineM30Setting['bar_interval'] = 30
|
||||||
|
lineM30Setting['para_pre_len'] = 10
|
||||||
|
lineM30Setting['para_ma1_len'] = 5
|
||||||
|
lineM30Setting['para_ma2_len'] = 10
|
||||||
|
lineM30Setting['para_ma3_len'] = 60
|
||||||
|
lineM30Setting['para_active_yb'] = True
|
||||||
|
lineM30Setting['para_active_skd'] = True
|
||||||
|
lineM30Setting['price_tick'] = self.price_tick
|
||||||
|
lineM30Setting['underlying_symbol'] = self.underlying_symbol
|
||||||
|
self.lineM30 = CtaMinuteBar(self, self.onBarM30, lineM30Setting)
|
||||||
|
|
||||||
|
# 写入文件
|
||||||
|
self.lineM30.export_filename = os.path.abspath(
|
||||||
|
os.path.join(os.getcwd(),
|
||||||
|
u'export_{}_{}.csv'.format(self.vt_symbol, self.lineM30.name)))
|
||||||
|
|
||||||
|
self.lineM30.export_fields = [
|
||||||
|
{'name': 'datetime', 'source': 'bar', 'attr': 'datetime', 'type_': 'datetime'},
|
||||||
|
{'name': 'open', 'source': 'bar', 'attr': 'open_price', 'type_': 'float'},
|
||||||
|
{'name': 'high', 'source': 'bar', 'attr': 'high_price', 'type_': 'float'},
|
||||||
|
{'name': 'low', 'source': 'bar', 'attr': 'low_price', 'type_': 'float'},
|
||||||
|
{'name': 'close', 'source': 'bar', 'attr': 'close_price', 'type_': 'float'},
|
||||||
|
{'name': 'turnover', 'source': 'bar', 'attr': 'turnover', 'type_': 'float'},
|
||||||
|
{'name': 'volume', 'source': 'bar', 'attr': 'volume', 'type_': 'float'},
|
||||||
|
{'name': 'open_interest', 'source': 'bar', 'attr': 'open_interest', 'type_': 'float'},
|
||||||
|
{'name': 'kf', 'source': 'line_bar', 'attr': 'line_statemean', 'type_': 'list'}
|
||||||
|
]
|
||||||
|
|
||||||
|
def createLineH1(self):
|
||||||
|
# 创建2小时K线
|
||||||
|
lineH1Setting = {}
|
||||||
|
lineH1Setting['name'] = u'H1'
|
||||||
|
lineH1Setting['interval'] = Interval.HOUR
|
||||||
|
lineH1Setting['bar_interval'] = 1
|
||||||
|
lineH1Setting['para_pre_len'] = 10
|
||||||
|
lineH1Setting['para_ema1_len'] = 5
|
||||||
|
lineH1Setting['para_ema2_len'] = 10
|
||||||
|
lineH1Setting['para_ema3_len'] = 60
|
||||||
|
lineH1Setting['para_active_yb'] = True
|
||||||
|
lineH1Setting['para_active_skd'] = True
|
||||||
|
lineH1Setting['price_tick'] = self.price_tick
|
||||||
|
lineH1Setting['underlying_symbol'] = self.underlying_symbol
|
||||||
|
self.lineH1 = CtaLineBar(self, self.onBarH1, lineH1Setting)
|
||||||
|
|
||||||
|
def createLineH2(self):
|
||||||
|
# 创建2小时K线
|
||||||
|
lineH2Setting = {}
|
||||||
|
lineH2Setting['name'] = u'H2'
|
||||||
|
lineH2Setting['interval'] = Interval.HOUR
|
||||||
|
lineH2Setting['bar_interval'] = 2
|
||||||
|
lineH2Setting['para_pre_len'] = 5
|
||||||
|
lineH2Setting['para_ma1_len'] = 5
|
||||||
|
lineH2Setting['para_ma2_len'] = 10
|
||||||
|
lineH2Setting['para_ma3_len'] = 18
|
||||||
|
lineH2Setting['para_active_yb'] = True
|
||||||
|
lineH2Setting['para_active_skd'] = True
|
||||||
|
lineH2Setting['mode'] = CtaLineBar.TICK_MODE
|
||||||
|
lineH2Setting['price_tick'] = self.price_tick
|
||||||
|
lineH2Setting['underlying_symbol'] = self.underlying_symbol
|
||||||
|
self.lineH2 = CtaHourBar(self, self.onBarH2, lineH2Setting)
|
||||||
|
|
||||||
|
def createLineD(self):
|
||||||
|
# 创建的日K线
|
||||||
|
lineDaySetting = {}
|
||||||
|
lineDaySetting['name'] = u'D1'
|
||||||
|
lineDaySetting['bar_interval'] = 1
|
||||||
|
lineDaySetting['para_pre_len'] = 5
|
||||||
|
lineDaySetting['para_art1_len'] = 26
|
||||||
|
lineDaySetting['para_ma1_len'] = 5
|
||||||
|
lineDaySetting['para_ma2_len'] = 10
|
||||||
|
lineDaySetting['para_ma3_len'] = 18
|
||||||
|
lineDaySetting['para_active_yb'] = True
|
||||||
|
lineDaySetting['para_active_skd'] = True
|
||||||
|
lineDaySetting['price_tick'] = self.price_tick
|
||||||
|
lineDaySetting['underlying_symbol'] = self.underlying_symbol
|
||||||
|
self.lineD = CtaDayBar(self, self.onBarD, lineDaySetting)
|
||||||
|
|
||||||
|
def createLineW(self):
|
||||||
|
"""创建周线"""
|
||||||
|
lineWeekSetting = {}
|
||||||
|
lineWeekSetting['name'] = u'W1'
|
||||||
|
lineWeekSetting['para_pre_len'] = 5
|
||||||
|
lineWeekSetting['para_art1_len'] = 26
|
||||||
|
lineWeekSetting['para_ma1_len'] = 5
|
||||||
|
lineWeekSetting['para_ma2_len'] = 10
|
||||||
|
lineWeekSetting['para_ma3_len'] = 18
|
||||||
|
lineWeekSetting['para_active_yb'] = True
|
||||||
|
lineWeekSetting['para_active_skd'] = True
|
||||||
|
lineWeekSetting['mode'] = CtaDayBar.TICK_MODE
|
||||||
|
lineWeekSetting['price_tick'] = self.price_tick
|
||||||
|
lineWeekSetting['underlying_symbol'] = self.underlying_symbol
|
||||||
|
self.lineW = CtaWeekBar(self, self.onBarW, lineWeekSetting)
|
||||||
|
|
||||||
|
def onBar(self, bar):
|
||||||
|
# print(u'tradingDay:{},dt:{},o:{},h:{},l:{},c:{},v:{}'.format(bar.trading_day,bar.datetime, bar.open, bar.high, bar.low_price, bar.close_price, bar.volume))
|
||||||
|
if self.lineW:
|
||||||
|
self.lineW.add_bar(bar, bar_freq=self.TMinuteInterval)
|
||||||
|
if self.lineD:
|
||||||
|
self.lineD.add_bar(bar, bar_freq=self.TMinuteInterval)
|
||||||
|
if self.lineH2:
|
||||||
|
self.lineH2.add_bar(bar, bar_freq=self.TMinuteInterval)
|
||||||
|
|
||||||
|
if self.lineH1:
|
||||||
|
self.lineH1.add_bar(bar, bar_freq=self.TMinuteInterval)
|
||||||
|
|
||||||
|
if self.lineM30:
|
||||||
|
self.lineM30.add_bar(bar, bar_freq=self.TMinuteInterval)
|
||||||
|
|
||||||
|
if self.lineM5:
|
||||||
|
self.lineM5.add_bar(bar, bar_freq=self.TMinuteInterval)
|
||||||
|
|
||||||
|
# if self.lineH2:
|
||||||
|
# self.lineH2.skd_is_high_dead_cross(runtime=True, high_skd=30)
|
||||||
|
# self.lineH2.skd_is_low_golden_cross(runtime=True, low_skd=70)
|
||||||
|
|
||||||
|
def onBarM30(self, bar):
|
||||||
|
self.write_log(self.lineM30.get_last_bar_str())
|
||||||
|
|
||||||
|
self.save_m30_bars.append({
|
||||||
|
'datetime': bar.datetime,
|
||||||
|
'open': bar.open_price,
|
||||||
|
'high': bar.high_price,
|
||||||
|
'low': bar.low_price,
|
||||||
|
'close': bar.close_price,
|
||||||
|
'turnover': 0,
|
||||||
|
'volume': bar.volume,
|
||||||
|
'open_interest': 0,
|
||||||
|
'ma5': self.lineM30.line_ma1[-1] if len(self.lineM30.line_ma1) > 0 else bar.close_price,
|
||||||
|
'ma10': self.lineM30.line_ma2[-1] if len(self.lineM30.line_ma2) > 0 else bar.close_price,
|
||||||
|
'ma60': self.lineM30.line_ma3[-1] if len(self.lineM30.line_ma3) > 0 else bar.close_price,
|
||||||
|
'sk': self.lineM30.line_sk[-1] if len(self.lineM30.line_sk) > 0 else 0,
|
||||||
|
'sd': self.lineM30.line_sd[-1] if len(self.lineM30.line_sd) > 0 else 0
|
||||||
|
})
|
||||||
|
|
||||||
|
def onBarH1(self, bar):
|
||||||
|
self.write_log(self.lineH1.get_last_bar_str())
|
||||||
|
|
||||||
|
self.save_h1_bars.append({
|
||||||
|
'datetime': bar.datetime,
|
||||||
|
'open': bar.open_price,
|
||||||
|
'high': bar.high_price,
|
||||||
|
'low': bar.low_price,
|
||||||
|
'close': bar.close_price,
|
||||||
|
'turnover': 0,
|
||||||
|
'volume': bar.volume,
|
||||||
|
'open_interest': 0,
|
||||||
|
'ema5': self.lineH1.line_ema1[-1] if len(self.lineH1.line_ema1) > 0 else bar.close_price,
|
||||||
|
'ema10': self.lineH1.line_ema2[-1] if len(self.lineH1.line_ema2) > 0 else bar.close_price,
|
||||||
|
'ema60': self.lineH1.line_ema3[-1] if len(self.lineH1.line_ema3) > 0 else bar.close_price,
|
||||||
|
'sk': self.lineH1.line_sk[-1] if len(self.lineH1.line_sk) > 0 else 0,
|
||||||
|
'sd': self.lineH1.line_sd[-1] if len(self.lineH1.line_sd) > 0 else 0
|
||||||
|
})
|
||||||
|
|
||||||
|
def onBarH2(self, bar):
|
||||||
|
self.write_log(self.lineH2.get_last_bar_str())
|
||||||
|
|
||||||
|
self.save_h2_bars.append({
|
||||||
|
'datetime': bar.datetime,
|
||||||
|
'open': bar.open_price,
|
||||||
|
'high': bar.high_price,
|
||||||
|
'low': bar.low_price,
|
||||||
|
'close': bar.close_price,
|
||||||
|
'turnover': 0,
|
||||||
|
'volume': bar.volume,
|
||||||
|
'open_interest': 0,
|
||||||
|
'ma5': self.lineH2.line_ma1[-1] if len(self.lineH2.line_ma1) > 0 else bar.close_price,
|
||||||
|
'ma10': self.lineH2.line_ma2[-1] if len(self.lineH2.line_ma2) > 0 else bar.close_price,
|
||||||
|
'ma18': self.lineH2.line_ma3[-1] if len(self.lineH2.line_ma3) > 0 else bar.close_price,
|
||||||
|
'sk': self.lineH2.line_sk[-1] if len(self.lineH2.line_sk) > 0 else 0,
|
||||||
|
'sd': self.lineH2.line_sd[-1] if len(self.lineH2.line_sd) > 0 else 0
|
||||||
|
})
|
||||||
|
|
||||||
|
def onBarD(self, bar):
|
||||||
|
self.write_log(self.lineD.get_last_bar_str())
|
||||||
|
self.save_d_bars.append({
|
||||||
|
'datetime': bar.datetime,
|
||||||
|
'open': bar.open_price,
|
||||||
|
'high': bar.high_price,
|
||||||
|
'low': bar.low_price,
|
||||||
|
'close': bar.close_price,
|
||||||
|
'turnover': 0,
|
||||||
|
'volume': bar.volume,
|
||||||
|
'open_interest': 0,
|
||||||
|
'ma5': self.lineD.line_ma1[-1] if len(self.lineD.line_ma1) > 0 else bar.close_price,
|
||||||
|
'ma10': self.lineD.line_ma2[-1] if len(self.lineD.line_ma2) > 0 else bar.close_price,
|
||||||
|
'ma18': self.lineD.line_ma3[-1] if len(self.lineD.line_ma3) > 0 else bar.close_price,
|
||||||
|
'sk': self.lineD.line_sk[-1] if len(self.lineD.line_sk) > 0 else 0,
|
||||||
|
'sd': self.lineD.line_sd[-1] if len(self.lineD.line_sd) > 0 else 0
|
||||||
|
})
|
||||||
|
|
||||||
|
def onBarW(self, bar):
|
||||||
|
self.write_log(self.lineW.get_last_bar_str())
|
||||||
|
self.save_w_bars.append({
|
||||||
|
'datetime': bar.datetime,
|
||||||
|
'open': bar.open_price,
|
||||||
|
'high': bar.high_price,
|
||||||
|
'low': bar.low_price,
|
||||||
|
'close': bar.close_price,
|
||||||
|
'turnover': 0,
|
||||||
|
'volume': bar.volume,
|
||||||
|
'open_interest': 0,
|
||||||
|
'ma5': self.lineW.line_ma1[-1] if len(self.lineW.line_ma1) > 0 else bar.close_price,
|
||||||
|
'ma10': self.lineW.line_ma2[-1] if len(self.lineW.line_ma2) > 0 else bar.close_price,
|
||||||
|
'ma18': self.lineW.line_ma3[-1] if len(self.lineW.line_ma3) > 0 else bar.close_price,
|
||||||
|
'sk': self.lineW.line_sk[-1] if len(self.lineW.line_sk) > 0 else 0,
|
||||||
|
'sd': self.lineW.line_sd[-1] if len(self.lineW.line_sd) > 0 else 0
|
||||||
|
})
|
||||||
|
|
||||||
|
def on_tick(self, tick):
|
||||||
|
print(u'{0},{1},ap:{2},av:{3},bp:{4},bv:{5}'.format(tick.datetime, tick.last_price, tick.ask_price_1,
|
||||||
|
tick.ask_volume_1, tick.bid_price_1, tick.bid_volume_1))
|
||||||
|
|
||||||
|
def write_log(self, content):
|
||||||
|
print(content)
|
||||||
|
|
||||||
|
def saveData(self):
|
||||||
|
|
||||||
|
if len(self.save_m30_bars) > 0:
|
||||||
|
outputFile = '{}_m30.csv'.format(self.vt_symbol)
|
||||||
|
with open(outputFile, 'w', encoding='utf8', newline='') as f:
|
||||||
|
fieldnames = ['datetime', 'open', 'high', 'low', 'close', 'turnover', 'volume', 'open_interest',
|
||||||
|
'ma5', 'ma10', 'ma60', 'sk', 'sd']
|
||||||
|
writer = csv.DictWriter(f=f, fieldnames=fieldnames, dialect='excel')
|
||||||
|
writer.writeheader()
|
||||||
|
for row in self.save_m30_bars:
|
||||||
|
writer.writerow(row)
|
||||||
|
|
||||||
|
if len(self.save_h1_bars) > 0:
|
||||||
|
outputFile = '{}_h1.csv'.format(self.vt_symbol)
|
||||||
|
with open(outputFile, 'w', encoding='utf8', newline='') as f:
|
||||||
|
fieldnames = ['datetime', 'open', 'high', 'low', 'close', 'turnover', 'volume', 'open_interest',
|
||||||
|
'ema5', 'ema10', 'ema60', 'sk', 'sd']
|
||||||
|
writer = csv.DictWriter(f=f, fieldnames=fieldnames, dialect='excel')
|
||||||
|
writer.writeheader()
|
||||||
|
for row in self.save_h1_bars:
|
||||||
|
writer.writerow(row)
|
||||||
|
|
||||||
|
if len(self.save_h2_bars) > 0:
|
||||||
|
outputFile = '{}_h2.csv'.format(self.vt_symbol)
|
||||||
|
with open(outputFile, 'w', encoding='utf8', newline='') as f:
|
||||||
|
fieldnames = ['datetime', 'open', 'high', 'low', 'close', 'turnover', 'volume', 'open_interest',
|
||||||
|
'ma5', 'ma10', 'ma18', 'sk', 'sd']
|
||||||
|
writer = csv.DictWriter(f=f, fieldnames=fieldnames, dialect='excel')
|
||||||
|
writer.writeheader()
|
||||||
|
for row in self.save_h2_bars:
|
||||||
|
writer.writerow(row)
|
||||||
|
|
||||||
|
if len(self.save_d_bars) > 0:
|
||||||
|
outputFile = '{}_d.csv'.format(self.vt_symbol)
|
||||||
|
with open(outputFile, 'w', encoding='utf8', newline='') as f:
|
||||||
|
fieldnames = ['datetime', 'open', 'high', 'low', 'close', 'turnover', 'volume', 'open_interest',
|
||||||
|
'ma5', 'ma10', 'ma18', 'sk', 'sd']
|
||||||
|
writer = csv.DictWriter(f=f, fieldnames=fieldnames, dialect='excel')
|
||||||
|
writer.writeheader()
|
||||||
|
for row in self.save_d_bars:
|
||||||
|
writer.writerow(row)
|
||||||
|
|
||||||
|
if len(self.save_w_bars) > 0:
|
||||||
|
outputFile = '{}_w.csv'.format(self.vt_symbol)
|
||||||
|
with open(outputFile, 'w', encoding='utf8', newline='') as f:
|
||||||
|
fieldnames = ['datetime', 'open', 'high', 'low', 'close', 'turnover', 'volume', 'open_interest',
|
||||||
|
'ma5', 'ma10', 'ma18', 'sk', 'sd']
|
||||||
|
writer = csv.DictWriter(f=f, fieldnames=fieldnames, dialect='excel')
|
||||||
|
writer.writeheader()
|
||||||
|
for row in self.save_w_bars:
|
||||||
|
writer.writerow(row)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
t = test_strategy()
|
||||||
|
t.price_tick = 0.5
|
||||||
|
t.underlying_symbol = 'J'
|
||||||
|
t.vt_symbol = 'J99'
|
||||||
|
|
||||||
|
# t.createM5()
|
||||||
|
# t.createLineW()
|
||||||
|
|
||||||
|
# t.createlineM30_with_macd()
|
||||||
|
|
||||||
|
# 创建M30线
|
||||||
|
# t.createLineM30()
|
||||||
|
|
||||||
|
# 回测1小时线
|
||||||
|
# t.createLineH1()
|
||||||
|
|
||||||
|
# 回测2小时线
|
||||||
|
# t.createLineH2()
|
||||||
|
|
||||||
|
# 回测日线
|
||||||
|
# t.createLineD()
|
||||||
|
|
||||||
|
# 测试周线
|
||||||
|
t.createLineW()
|
||||||
|
|
||||||
|
# vnpy/app/cta_strategy_pro/
|
||||||
|
vnpy_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
|
||||||
|
|
||||||
|
filename = os.path.abspath(os.path.join(vnpy_root, 'bar_data/{}_20160101_1m.csv'.format(t.vt_symbol)))
|
||||||
|
csv_bar_seconds = 60 # csv 文件内,bar的时间间隔60秒
|
||||||
|
|
||||||
|
import csv
|
||||||
|
|
||||||
|
csvfile = open(filename, 'r', encoding='utf8')
|
||||||
|
reader = csv.DictReader((line.replace('\0', '') for line in csvfile), delimiter=",")
|
||||||
|
last_tradingDay = None
|
||||||
|
for row in reader:
|
||||||
|
try:
|
||||||
|
dt = datetime.strptime(row['datetime'], '%Y-%m-%d %H:%M:%S') - timedelta(seconds=csv_bar_seconds)
|
||||||
|
|
||||||
|
bar = BarData(
|
||||||
|
gateway_name='',
|
||||||
|
symbol=t.vt_symbol,
|
||||||
|
exchange=Exchange.LOCAL,
|
||||||
|
datetime=dt,
|
||||||
|
interval=Interval.MINUTE,
|
||||||
|
open_price=round_to(float(row['open']), t.price_tick),
|
||||||
|
high_price=round_to(float(row['high']), t.price_tick),
|
||||||
|
low_price=round_to(float(row['low']), t.price_tick),
|
||||||
|
close_price=round_to(float(row['close']), t.price_tick),
|
||||||
|
volume=float(row['volume'])
|
||||||
|
)
|
||||||
|
|
||||||
|
if 'trading_date' in row:
|
||||||
|
bar.trading_day = row['trading_date']
|
||||||
|
if len(bar.trading_day) == 8 and '-' not in bar.trading_day:
|
||||||
|
bar.trading_day = bar.trading_day[0:4] + '-' + bar.trading_day[4:6] + '-' + bar.trading_day[6:8]
|
||||||
|
else:
|
||||||
|
if bar.datetime.hour >= 21:
|
||||||
|
if bar.datetime.isoweekday() == 5:
|
||||||
|
# 星期五=》星期一
|
||||||
|
bar.trading_day = (dt + timedelta(days=3)).strftime('%Y-%m-%d')
|
||||||
|
else:
|
||||||
|
# 第二天
|
||||||
|
bar.trading_day = (dt + timedelta(days=1)).strftime('%Y-%m-%d')
|
||||||
|
elif bar.datetime.hour < 8 and bar.datetime.isoweekday() == 6:
|
||||||
|
# 星期六=>星期一
|
||||||
|
bar.trading_day = (dt + timedelta(days=2)).strftime('%Y-%m-%d')
|
||||||
|
else:
|
||||||
|
bar.trading_day = bar.datetime.strftime('%Y-%m-%d')
|
||||||
|
|
||||||
|
t.onBar(bar)
|
||||||
|
# 测试 实时计算值
|
||||||
|
# sk, sd = t.lineM30.getRuntimeSKD()
|
||||||
|
|
||||||
|
# 测试实时计算值
|
||||||
|
# if bar.datetime.minute==1:
|
||||||
|
# print('rt_Dif:{}'.format(t.lineM30.rt_Dif))
|
||||||
|
except Exception as ex:
|
||||||
|
t.write_log(u'{0}:{1}'.format(Exception, ex))
|
||||||
|
traceback.print_exc()
|
||||||
|
break
|
||||||
|
|
||||||
|
t.saveData()
|
@ -3,8 +3,6 @@
|
|||||||
# 通达信指数行情发布器
|
# 通达信指数行情发布器
|
||||||
# 华富资产
|
# 华富资产
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
import traceback
|
import traceback
|
||||||
|
@ -11,7 +11,6 @@ from vnpy.trader.event import EVENT_TRADE, EVENT_ORDER, EVENT_LOG, EVENT_ACCOUNT
|
|||||||
from vnpy.trader.constant import Status
|
from vnpy.trader.constant import Status
|
||||||
from vnpy.trader.utility import load_json, save_json
|
from vnpy.trader.utility import load_json, save_json
|
||||||
|
|
||||||
|
|
||||||
APP_NAME = "RiskManager"
|
APP_NAME = "RiskManager"
|
||||||
|
|
||||||
|
|
||||||
@ -178,6 +177,24 @@ class RiskManagerEngine(BaseEngine):
|
|||||||
account.balance, account_percent, self.percent_limit)
|
account.balance, account_percent, self.percent_limit)
|
||||||
self.write_log(msg)
|
self.write_log(msg)
|
||||||
|
|
||||||
|
def get_account(self, vt_accountid: str = ""):
|
||||||
|
"""获取账号的当前净值,可用资金,账号当前仓位百分比,允许的最大仓位百分比"""
|
||||||
|
if vt_accountid:
|
||||||
|
account = self.account_dict.get(vt_accountid, None)
|
||||||
|
if account:
|
||||||
|
return account.balance, \
|
||||||
|
account.available, \
|
||||||
|
round(account.frozen * 100 / (account.balance + 0.01), 2), \
|
||||||
|
self.percent_limit
|
||||||
|
if len(self.account_dict.values()) > 0:
|
||||||
|
account = self.account_dict.values()[0]
|
||||||
|
return account.balance, \
|
||||||
|
account.available, \
|
||||||
|
round(account.frozen * 100 / (account.balance + 0.01), 2), \
|
||||||
|
self.percent_limit
|
||||||
|
else:
|
||||||
|
return 0, 0, 0, 0
|
||||||
|
|
||||||
def write_log(self, msg: str):
|
def write_log(self, msg: str):
|
||||||
""""""
|
""""""
|
||||||
log = LogData(msg=msg, gateway_name="RiskManager")
|
log = LogData(msg=msg, gateway_name="RiskManager")
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
"mi_symbol": "ag2007",
|
"mi_symbol": "ag2007",
|
||||||
"full_symbol": "AG2007",
|
"full_symbol": "AG2007",
|
||||||
"exchange": "SHFE",
|
"exchange": "SHFE",
|
||||||
"margin_rate": 0.08,
|
"margin_rate": 0.07,
|
||||||
"symbol_size": 15,
|
"symbol_size": 15,
|
||||||
"price_tick": 1.0
|
"price_tick": 1.0
|
||||||
},
|
},
|
||||||
@ -22,7 +22,7 @@
|
|||||||
"mi_symbol": "al2003",
|
"mi_symbol": "al2003",
|
||||||
"full_symbol": "AL2003",
|
"full_symbol": "AL2003",
|
||||||
"exchange": "SHFE",
|
"exchange": "SHFE",
|
||||||
"margin_rate": 0.1,
|
"margin_rate": 0.07,
|
||||||
"symbol_size": 5,
|
"symbol_size": 5,
|
||||||
"price_tick": 5.0
|
"price_tick": 5.0
|
||||||
},
|
},
|
||||||
@ -31,7 +31,7 @@
|
|||||||
"mi_symbol": "AP005",
|
"mi_symbol": "AP005",
|
||||||
"full_symbol": "AP2005",
|
"full_symbol": "AP2005",
|
||||||
"exchange": "CZCE",
|
"exchange": "CZCE",
|
||||||
"margin_rate": 0.07,
|
"margin_rate": 0.08,
|
||||||
"symbol_size": 10,
|
"symbol_size": 10,
|
||||||
"price_tick": 1.0
|
"price_tick": 1.0
|
||||||
},
|
},
|
||||||
@ -40,7 +40,7 @@
|
|||||||
"mi_symbol": "au2006",
|
"mi_symbol": "au2006",
|
||||||
"full_symbol": "AU2006",
|
"full_symbol": "AU2006",
|
||||||
"exchange": "SHFE",
|
"exchange": "SHFE",
|
||||||
"margin_rate": 0.08,
|
"margin_rate": 0.06,
|
||||||
"symbol_size": 1000,
|
"symbol_size": 1000,
|
||||||
"price_tick": 0.02
|
"price_tick": 0.02
|
||||||
},
|
},
|
||||||
@ -55,8 +55,8 @@
|
|||||||
},
|
},
|
||||||
"BB": {
|
"BB": {
|
||||||
"underlying_symbol": "BB",
|
"underlying_symbol": "BB",
|
||||||
"mi_symbol": "bb2012",
|
"mi_symbol": "bb2101",
|
||||||
"full_symbol": "BB2012",
|
"full_symbol": "BB2101",
|
||||||
"exchange": "DCE",
|
"exchange": "DCE",
|
||||||
"margin_rate": 0.2,
|
"margin_rate": 0.2,
|
||||||
"symbol_size": 500,
|
"symbol_size": 500,
|
||||||
@ -67,7 +67,7 @@
|
|||||||
"mi_symbol": "bu2006",
|
"mi_symbol": "bu2006",
|
||||||
"full_symbol": "BU2006",
|
"full_symbol": "BU2006",
|
||||||
"exchange": "SHFE",
|
"exchange": "SHFE",
|
||||||
"margin_rate": 0.1,
|
"margin_rate": 0.09,
|
||||||
"symbol_size": 10,
|
"symbol_size": 10,
|
||||||
"price_tick": 2.0
|
"price_tick": 2.0
|
||||||
},
|
},
|
||||||
@ -82,8 +82,8 @@
|
|||||||
},
|
},
|
||||||
"CF": {
|
"CF": {
|
||||||
"underlying_symbol": "CF",
|
"underlying_symbol": "CF",
|
||||||
"mi_symbol": "CF005",
|
"mi_symbol": "CF009",
|
||||||
"full_symbol": "CF2005",
|
"full_symbol": "CF2009",
|
||||||
"exchange": "CZCE",
|
"exchange": "CZCE",
|
||||||
"margin_rate": 0.05,
|
"margin_rate": 0.05,
|
||||||
"symbol_size": 5,
|
"symbol_size": 5,
|
||||||
@ -112,7 +112,7 @@
|
|||||||
"mi_symbol": "cu2003",
|
"mi_symbol": "cu2003",
|
||||||
"full_symbol": "CU2003",
|
"full_symbol": "CU2003",
|
||||||
"exchange": "SHFE",
|
"exchange": "SHFE",
|
||||||
"margin_rate": 0.09,
|
"margin_rate": 0.07,
|
||||||
"symbol_size": 5,
|
"symbol_size": 5,
|
||||||
"price_tick": 10.0
|
"price_tick": 10.0
|
||||||
},
|
},
|
||||||
@ -139,7 +139,7 @@
|
|||||||
"mi_symbol": "eg2005",
|
"mi_symbol": "eg2005",
|
||||||
"full_symbol": "EG2005",
|
"full_symbol": "EG2005",
|
||||||
"exchange": "DCE",
|
"exchange": "DCE",
|
||||||
"margin_rate": 0.05,
|
"margin_rate": 0.06,
|
||||||
"symbol_size": 10,
|
"symbol_size": 10,
|
||||||
"price_tick": 1.0
|
"price_tick": 1.0
|
||||||
},
|
},
|
||||||
@ -148,9 +148,9 @@
|
|||||||
"mi_symbol": "fb2005",
|
"mi_symbol": "fb2005",
|
||||||
"full_symbol": "FB2005",
|
"full_symbol": "FB2005",
|
||||||
"exchange": "DCE",
|
"exchange": "DCE",
|
||||||
"margin_rate": 0.2,
|
"margin_rate": 0.1,
|
||||||
"symbol_size": 500,
|
"symbol_size": 10,
|
||||||
"price_tick": 0.05
|
"price_tick": 0.5
|
||||||
},
|
},
|
||||||
"FG": {
|
"FG": {
|
||||||
"underlying_symbol": "FG",
|
"underlying_symbol": "FG",
|
||||||
@ -166,7 +166,7 @@
|
|||||||
"mi_symbol": "fu2005",
|
"mi_symbol": "fu2005",
|
||||||
"full_symbol": "FU2005",
|
"full_symbol": "FU2005",
|
||||||
"exchange": "SHFE",
|
"exchange": "SHFE",
|
||||||
"margin_rate": 0.2,
|
"margin_rate": 0.1,
|
||||||
"symbol_size": 10,
|
"symbol_size": 10,
|
||||||
"price_tick": 1.0
|
"price_tick": 1.0
|
||||||
},
|
},
|
||||||
@ -175,7 +175,7 @@
|
|||||||
"mi_symbol": "hc2005",
|
"mi_symbol": "hc2005",
|
||||||
"full_symbol": "HC2005",
|
"full_symbol": "HC2005",
|
||||||
"exchange": "SHFE",
|
"exchange": "SHFE",
|
||||||
"margin_rate": 0.1,
|
"margin_rate": 0.08,
|
||||||
"symbol_size": 10,
|
"symbol_size": 10,
|
||||||
"price_tick": 1.0
|
"price_tick": 1.0
|
||||||
},
|
},
|
||||||
@ -184,7 +184,7 @@
|
|||||||
"mi_symbol": "i2005",
|
"mi_symbol": "i2005",
|
||||||
"full_symbol": "I2005",
|
"full_symbol": "I2005",
|
||||||
"exchange": "DCE",
|
"exchange": "DCE",
|
||||||
"margin_rate": 0.05,
|
"margin_rate": 0.08,
|
||||||
"symbol_size": 100,
|
"symbol_size": 100,
|
||||||
"price_tick": 0.5
|
"price_tick": 0.5
|
||||||
},
|
},
|
||||||
@ -193,7 +193,7 @@
|
|||||||
"mi_symbol": "IC2003",
|
"mi_symbol": "IC2003",
|
||||||
"full_symbol": "IC2003",
|
"full_symbol": "IC2003",
|
||||||
"exchange": "CFFEX",
|
"exchange": "CFFEX",
|
||||||
"margin_rate": 0.1,
|
"margin_rate": 0.12,
|
||||||
"symbol_size": 200,
|
"symbol_size": 200,
|
||||||
"price_tick": 0.2
|
"price_tick": 0.2
|
||||||
},
|
},
|
||||||
@ -220,7 +220,7 @@
|
|||||||
"mi_symbol": "j2005",
|
"mi_symbol": "j2005",
|
||||||
"full_symbol": "J2005",
|
"full_symbol": "J2005",
|
||||||
"exchange": "DCE",
|
"exchange": "DCE",
|
||||||
"margin_rate": 0.05,
|
"margin_rate": 0.08,
|
||||||
"symbol_size": 100,
|
"symbol_size": 100,
|
||||||
"price_tick": 0.5
|
"price_tick": 0.5
|
||||||
},
|
},
|
||||||
@ -229,7 +229,7 @@
|
|||||||
"mi_symbol": "jd2005",
|
"mi_symbol": "jd2005",
|
||||||
"full_symbol": "JD2005",
|
"full_symbol": "JD2005",
|
||||||
"exchange": "DCE",
|
"exchange": "DCE",
|
||||||
"margin_rate": 0.08,
|
"margin_rate": 0.07,
|
||||||
"symbol_size": 10,
|
"symbol_size": 10,
|
||||||
"price_tick": 1.0
|
"price_tick": 1.0
|
||||||
},
|
},
|
||||||
@ -238,14 +238,14 @@
|
|||||||
"mi_symbol": "jm2005",
|
"mi_symbol": "jm2005",
|
||||||
"full_symbol": "JM2005",
|
"full_symbol": "JM2005",
|
||||||
"exchange": "DCE",
|
"exchange": "DCE",
|
||||||
"margin_rate": 0.05,
|
"margin_rate": 0.08,
|
||||||
"symbol_size": 60,
|
"symbol_size": 60,
|
||||||
"price_tick": 0.5
|
"price_tick": 0.5
|
||||||
},
|
},
|
||||||
"JR": {
|
"JR": {
|
||||||
"underlying_symbol": "JR",
|
"underlying_symbol": "JR",
|
||||||
"mi_symbol": "JR011",
|
"mi_symbol": "JR101",
|
||||||
"full_symbol": "JR2011",
|
"full_symbol": "JR2101",
|
||||||
"exchange": "CZCE",
|
"exchange": "CZCE",
|
||||||
"margin_rate": 0.05,
|
"margin_rate": 0.05,
|
||||||
"symbol_size": 20,
|
"symbol_size": 20,
|
||||||
@ -328,14 +328,14 @@
|
|||||||
"mi_symbol": "pb2003",
|
"mi_symbol": "pb2003",
|
||||||
"full_symbol": "PB2003",
|
"full_symbol": "PB2003",
|
||||||
"exchange": "SHFE",
|
"exchange": "SHFE",
|
||||||
"margin_rate": 0.1,
|
"margin_rate": 0.07,
|
||||||
"symbol_size": 5,
|
"symbol_size": 5,
|
||||||
"price_tick": 5.0
|
"price_tick": 5.0
|
||||||
},
|
},
|
||||||
"PM": {
|
"PM": {
|
||||||
"underlying_symbol": "PM",
|
"underlying_symbol": "PM",
|
||||||
"mi_symbol": "PM011",
|
"mi_symbol": "PM101",
|
||||||
"full_symbol": "PM2011",
|
"full_symbol": "PM2101",
|
||||||
"exchange": "CZCE",
|
"exchange": "CZCE",
|
||||||
"margin_rate": 0.05,
|
"margin_rate": 0.05,
|
||||||
"symbol_size": 50,
|
"symbol_size": 50,
|
||||||
@ -355,14 +355,14 @@
|
|||||||
"mi_symbol": "rb2005",
|
"mi_symbol": "rb2005",
|
||||||
"full_symbol": "RB2005",
|
"full_symbol": "RB2005",
|
||||||
"exchange": "SHFE",
|
"exchange": "SHFE",
|
||||||
"margin_rate": 0.1,
|
"margin_rate": 0.08,
|
||||||
"symbol_size": 10,
|
"symbol_size": 10,
|
||||||
"price_tick": 1.0
|
"price_tick": 1.0
|
||||||
},
|
},
|
||||||
"RI": {
|
"RI": {
|
||||||
"underlying_symbol": "RI",
|
"underlying_symbol": "RI",
|
||||||
"mi_symbol": "RI011",
|
"mi_symbol": "RI101",
|
||||||
"full_symbol": "RI2011",
|
"full_symbol": "RI2101",
|
||||||
"exchange": "CZCE",
|
"exchange": "CZCE",
|
||||||
"margin_rate": 0.05,
|
"margin_rate": 0.05,
|
||||||
"symbol_size": 20,
|
"symbol_size": 20,
|
||||||
@ -373,7 +373,7 @@
|
|||||||
"mi_symbol": "RM005",
|
"mi_symbol": "RM005",
|
||||||
"full_symbol": "RM2005",
|
"full_symbol": "RM2005",
|
||||||
"exchange": "CZCE",
|
"exchange": "CZCE",
|
||||||
"margin_rate": 0.05,
|
"margin_rate": 0.06,
|
||||||
"symbol_size": 10,
|
"symbol_size": 10,
|
||||||
"price_tick": 1.0
|
"price_tick": 1.0
|
||||||
},
|
},
|
||||||
@ -391,7 +391,7 @@
|
|||||||
"mi_symbol": "RS011",
|
"mi_symbol": "RS011",
|
||||||
"full_symbol": "RS2011",
|
"full_symbol": "RS2011",
|
||||||
"exchange": "CZCE",
|
"exchange": "CZCE",
|
||||||
"margin_rate": 0.05,
|
"margin_rate": 0.2,
|
||||||
"symbol_size": 10,
|
"symbol_size": 10,
|
||||||
"price_tick": 1.0
|
"price_tick": 1.0
|
||||||
},
|
},
|
||||||
@ -400,7 +400,7 @@
|
|||||||
"mi_symbol": "ru2005",
|
"mi_symbol": "ru2005",
|
||||||
"full_symbol": "RU2005",
|
"full_symbol": "RU2005",
|
||||||
"exchange": "SHFE",
|
"exchange": "SHFE",
|
||||||
"margin_rate": 0.1,
|
"margin_rate": 0.09,
|
||||||
"symbol_size": 10,
|
"symbol_size": 10,
|
||||||
"price_tick": 5.0
|
"price_tick": 5.0
|
||||||
},
|
},
|
||||||
@ -418,7 +418,7 @@
|
|||||||
"mi_symbol": "sc2003",
|
"mi_symbol": "sc2003",
|
||||||
"full_symbol": "SC2003",
|
"full_symbol": "SC2003",
|
||||||
"exchange": "INE",
|
"exchange": "INE",
|
||||||
"margin_rate": 0.05,
|
"margin_rate": 0.07,
|
||||||
"symbol_size": 1000,
|
"symbol_size": 1000,
|
||||||
"price_tick": 0.1
|
"price_tick": 0.1
|
||||||
},
|
},
|
||||||
@ -427,7 +427,7 @@
|
|||||||
"mi_symbol": "SF005",
|
"mi_symbol": "SF005",
|
||||||
"full_symbol": "SF2005",
|
"full_symbol": "SF2005",
|
||||||
"exchange": "CZCE",
|
"exchange": "CZCE",
|
||||||
"margin_rate": 0.05,
|
"margin_rate": 0.07,
|
||||||
"symbol_size": 5,
|
"symbol_size": 5,
|
||||||
"price_tick": 2.0
|
"price_tick": 2.0
|
||||||
},
|
},
|
||||||
@ -436,7 +436,7 @@
|
|||||||
"mi_symbol": "SM005",
|
"mi_symbol": "SM005",
|
||||||
"full_symbol": "SM2005",
|
"full_symbol": "SM2005",
|
||||||
"exchange": "CZCE",
|
"exchange": "CZCE",
|
||||||
"margin_rate": 0.05,
|
"margin_rate": 0.07,
|
||||||
"symbol_size": 5,
|
"symbol_size": 5,
|
||||||
"price_tick": 2.0
|
"price_tick": 2.0
|
||||||
},
|
},
|
||||||
@ -445,7 +445,7 @@
|
|||||||
"mi_symbol": "sn2006",
|
"mi_symbol": "sn2006",
|
||||||
"full_symbol": "SN2006",
|
"full_symbol": "SN2006",
|
||||||
"exchange": "SHFE",
|
"exchange": "SHFE",
|
||||||
"margin_rate": 0.09,
|
"margin_rate": 0.08,
|
||||||
"symbol_size": 1,
|
"symbol_size": 1,
|
||||||
"price_tick": 10.0
|
"price_tick": 10.0
|
||||||
},
|
},
|
||||||
@ -490,7 +490,7 @@
|
|||||||
"mi_symbol": "TA005",
|
"mi_symbol": "TA005",
|
||||||
"full_symbol": "TA2005",
|
"full_symbol": "TA2005",
|
||||||
"exchange": "CZCE",
|
"exchange": "CZCE",
|
||||||
"margin_rate": 0.05,
|
"margin_rate": 0.06,
|
||||||
"symbol_size": 5,
|
"symbol_size": 5,
|
||||||
"price_tick": 2.0
|
"price_tick": 2.0
|
||||||
},
|
},
|
||||||
@ -535,7 +535,7 @@
|
|||||||
"mi_symbol": "WH011",
|
"mi_symbol": "WH011",
|
||||||
"full_symbol": "WH2011",
|
"full_symbol": "WH2011",
|
||||||
"exchange": "CZCE",
|
"exchange": "CZCE",
|
||||||
"margin_rate": 0.05,
|
"margin_rate": 0.07,
|
||||||
"symbol_size": 20,
|
"symbol_size": 20,
|
||||||
"price_tick": 1.0
|
"price_tick": 1.0
|
||||||
},
|
},
|
||||||
@ -544,7 +544,7 @@
|
|||||||
"mi_symbol": "wr2012",
|
"mi_symbol": "wr2012",
|
||||||
"full_symbol": "WR2012",
|
"full_symbol": "WR2012",
|
||||||
"exchange": "SHFE",
|
"exchange": "SHFE",
|
||||||
"margin_rate": 0.2,
|
"margin_rate": 0.08,
|
||||||
"symbol_size": 10,
|
"symbol_size": 10,
|
||||||
"price_tick": 1.0
|
"price_tick": 1.0
|
||||||
},
|
},
|
||||||
@ -562,7 +562,7 @@
|
|||||||
"mi_symbol": "ZC005",
|
"mi_symbol": "ZC005",
|
||||||
"full_symbol": "ZC2005",
|
"full_symbol": "ZC2005",
|
||||||
"exchange": "CZCE",
|
"exchange": "CZCE",
|
||||||
"margin_rate": 0.05,
|
"margin_rate": 0.06,
|
||||||
"symbol_size": 100,
|
"symbol_size": 100,
|
||||||
"price_tick": 0.2
|
"price_tick": 0.2
|
||||||
},
|
},
|
||||||
@ -571,7 +571,7 @@
|
|||||||
"mi_symbol": "zn2003",
|
"mi_symbol": "zn2003",
|
||||||
"full_symbol": "ZN2003",
|
"full_symbol": "ZN2003",
|
||||||
"exchange": "SHFE",
|
"exchange": "SHFE",
|
||||||
"margin_rate": 0.1,
|
"margin_rate": 0.07,
|
||||||
"symbol_size": 5,
|
"symbol_size": 5,
|
||||||
"price_tick": 5.0
|
"price_tick": 5.0
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ api_01 = TdxFutureData()
|
|||||||
api_01.update_mi_contracts()
|
api_01.update_mi_contracts()
|
||||||
|
|
||||||
# 逐一指数合约下载并更新
|
# 逐一指数合约下载并更新
|
||||||
for underlying_symbol in ['RB', 'J']: #api_01.future_contracts.keys():
|
for underlying_symbol in api_01.future_contracts.keys():
|
||||||
index_symbol = underlying_symbol + '99'
|
index_symbol = underlying_symbol + '99'
|
||||||
print(f'开始更新:{index_symbol}')
|
print(f'开始更新:{index_symbol}')
|
||||||
# csv数据文件名
|
# csv数据文件名
|
||||||
|
@ -137,7 +137,7 @@ class TdxFutureData(object):
|
|||||||
last_datetime = datetime.strptime(last_datetime_str, '%Y-%m-%d %H:%M:%S')
|
last_datetime = datetime.strptime(last_datetime_str, '%Y-%m-%d %H:%M:%S')
|
||||||
if (datetime.now() - last_datetime).total_seconds() > 60 * 60 * 2:
|
if (datetime.now() - last_datetime).total_seconds() > 60 * 60 * 2:
|
||||||
self.best_ip = {}
|
self.best_ip = {}
|
||||||
except Exception as ex:
|
except Exception as ex: # noqa
|
||||||
self.best_ip = {}
|
self.best_ip = {}
|
||||||
else:
|
else:
|
||||||
self.best_ip = {}
|
self.best_ip = {}
|
||||||
@ -262,6 +262,8 @@ class TdxFutureData(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
ret_bars = []
|
ret_bars = []
|
||||||
|
if '.' in symbol:
|
||||||
|
symbol = symbol.split('.')[0]
|
||||||
tdx_symbol = symbol.upper().replace('_', '')
|
tdx_symbol = symbol.upper().replace('_', '')
|
||||||
tdx_symbol = tdx_symbol.replace('99', 'L9')
|
tdx_symbol = tdx_symbol.replace('99', 'L9')
|
||||||
underlying_symbol = get_underlying_symbol(symbol).upper()
|
underlying_symbol = get_underlying_symbol(symbol).upper()
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
# Celery app
|
# Celery app
|
||||||
# 该py脚本,为启动celery worker app
|
# 该py脚本,为启动celery worker app
|
||||||
# 在项目根目录下,运行 celery -A vnpy.task.celery worker
|
# 在项目根目录下,运行 celery -A vnpy.task.celery worker
|
||||||
import time
|
|
||||||
from celery import Celery
|
from celery import Celery
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
@ -23,7 +22,7 @@ if vnpy_root not in sys.path:
|
|||||||
sys.path.append(vnpy_root)
|
sys.path.append(vnpy_root)
|
||||||
|
|
||||||
# 使用本地配置的
|
# 使用本地配置的
|
||||||
from vnpy.trader.utility import load_json
|
from vnpy.trader.utility import load_json # noqa
|
||||||
|
|
||||||
file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'celery_config.json'))
|
file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'celery_config.json'))
|
||||||
celery_config = load_json(file_path)
|
celery_config = load_json(file_path)
|
||||||
@ -40,17 +39,8 @@ print(u'Celery 使用redis配置:\nbroker:{}\nbackend:{}'.format(broker, backend
|
|||||||
|
|
||||||
app = Celery('vnpy_task', broker=broker)
|
app = Celery('vnpy_task', broker=broker)
|
||||||
|
|
||||||
|
|
||||||
# 动态导入task目录下子任务
|
# 动态导入task目录下子任务
|
||||||
# app.conf.CELERY_IMPORTS = ['vnpy.task.celery_app.worker_started']
|
app.conf.CELERY_IMPORTS = ['vnpy.task.celery_app.worker_started']
|
||||||
|
|
||||||
# app.conf.update(
|
|
||||||
# CELERY_TASK_SERIALIZER='json',
|
|
||||||
# CELERY_RESULT_SERIALIZER='json',
|
|
||||||
# CELERY_ACCEPT_CONTENT=['json'],
|
|
||||||
# CELERY_TIMEZONE='Asia/Shanghai',
|
|
||||||
# CELERY_ENABLE_UTC=True
|
|
||||||
# )
|
|
||||||
|
|
||||||
|
|
||||||
def worker_started():
|
def worker_started():
|
||||||
@ -59,7 +49,7 @@ def worker_started():
|
|||||||
import socket
|
import socket
|
||||||
from vnpy.trader.util_wechat import send_wx_msg
|
from vnpy.trader.util_wechat import send_wx_msg
|
||||||
send_wx_msg(u'{} Celery Worker 启动'.format(socket.gethostname()))
|
send_wx_msg(u'{} Celery Worker 启动'.format(socket.gethostname()))
|
||||||
except:
|
except: # noqa
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -136,6 +136,7 @@ class Exchange(Enum):
|
|||||||
LOCAL = "LOCAL" # For local generated data
|
LOCAL = "LOCAL" # For local generated data
|
||||||
SPD = "SPD" # Customer Spread data
|
SPD = "SPD" # Customer Spread data
|
||||||
|
|
||||||
|
|
||||||
class Currency(Enum):
|
class Currency(Enum):
|
||||||
"""
|
"""
|
||||||
Currency.
|
Currency.
|
||||||
|
@ -176,7 +176,7 @@ class MainEngine:
|
|||||||
if gateway:
|
if gateway:
|
||||||
gateway.subscribe(req)
|
gateway.subscribe(req)
|
||||||
else:
|
else:
|
||||||
for gateway in self.gateways.items():
|
for gateway in self.gateways.values():
|
||||||
if gateway:
|
if gateway:
|
||||||
gateway.subscribe(req)
|
gateway.subscribe(req)
|
||||||
|
|
||||||
@ -196,7 +196,8 @@ class MainEngine:
|
|||||||
"""
|
"""
|
||||||
gateway = self.get_gateway(gateway_name)
|
gateway = self.get_gateway(gateway_name)
|
||||||
if gateway:
|
if gateway:
|
||||||
gateway.cancel_order(req)
|
return gateway.cancel_order(req)
|
||||||
|
return False
|
||||||
|
|
||||||
def send_orders(self, reqs: Sequence[OrderRequest], gateway_name: str):
|
def send_orders(self, reqs: Sequence[OrderRequest], gateway_name: str):
|
||||||
"""
|
"""
|
||||||
@ -588,12 +589,13 @@ class CustomContract(object):
|
|||||||
for symbol, setting in self.setting.items():
|
for symbol, setting in self.setting.items():
|
||||||
gateway_name = setting.get('gateway_name', None)
|
gateway_name = setting.get('gateway_name', None)
|
||||||
if gateway_name is None:
|
if gateway_name is None:
|
||||||
gateway_name= SETTINGS.get('gateway_name','')
|
gateway_name = SETTINGS.get('gateway_name', '')
|
||||||
vn_exchange = Exchange(setting.get('exchange', 'LOCAL'))
|
vn_exchange = Exchange(setting.get('exchange', 'LOCAL'))
|
||||||
contract = ContractData(
|
contract = ContractData(
|
||||||
gateway_name=gateway_name,
|
gateway_name=gateway_name,
|
||||||
symbol=symbol,
|
symbol=symbol,
|
||||||
name=contract.symbol,
|
exchange=vn_exchange,
|
||||||
|
name=setting.get('name', symbol),
|
||||||
size=setting.get('size', 100),
|
size=setting.get('size', 100),
|
||||||
pricetick=setting.get('price_tick', 0.01),
|
pricetick=setting.get('price_tick', 0.01),
|
||||||
margin_rate=setting.get('margin_rate', 0.1)
|
margin_rate=setting.get('margin_rate', 0.1)
|
||||||
@ -602,6 +604,7 @@ class CustomContract(object):
|
|||||||
|
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
class EmailEngine(BaseEngine):
|
class EmailEngine(BaseEngine):
|
||||||
"""
|
"""
|
||||||
Provides email sending function for VN Trader.
|
Provides email sending function for VN Trader.
|
||||||
|
@ -26,4 +26,3 @@ EVENT_FUNDS_FLOW = 'eFundsFlow.'
|
|||||||
EVENT_ERROR = 'eError'
|
EVENT_ERROR = 'eError'
|
||||||
EVENT_WARNING = 'eWarning'
|
EVENT_WARNING = 'eWarning'
|
||||||
EVENT_CRITICAL = 'eCritical'
|
EVENT_CRITICAL = 'eCritical'
|
||||||
|
|
||||||
|
@ -262,7 +262,7 @@ class BaseGateway(ABC):
|
|||||||
implementation should finish the tasks blow:
|
implementation should finish the tasks blow:
|
||||||
* send request to server
|
* send request to server
|
||||||
"""
|
"""
|
||||||
pass
|
return False
|
||||||
|
|
||||||
def send_orders(self, reqs: Sequence[OrderRequest]):
|
def send_orders(self, reqs: Sequence[OrderRequest]):
|
||||||
"""
|
"""
|
||||||
|
@ -8,7 +8,7 @@ from logging import INFO
|
|||||||
|
|
||||||
from .constant import Direction, Exchange, Interval, Offset, Status, Product, OptionType, OrderType
|
from .constant import Direction, Exchange, Interval, Offset, Status, Product, OptionType, OrderType
|
||||||
|
|
||||||
ACTIVE_STATUSES = set([Status.SUBMITTING, Status.NOTTRADED, Status.PARTTRADED])
|
ACTIVE_STATUSES = set([Status.SUBMITTING, Status.NOTTRADED, Status.PARTTRADED, Status.CANCELLING])
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -339,6 +339,7 @@ class SubscribeRequest:
|
|||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self.vt_symbol == other.vt_symbol
|
return self.vt_symbol == other.vt_symbol
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class OrderRequest:
|
class OrderRequest:
|
||||||
"""
|
"""
|
||||||
|
@ -172,17 +172,20 @@ def extract_vt_symbol(vt_symbol: str):
|
|||||||
symbol, exchange_str = vt_symbol.split(".")
|
symbol, exchange_str = vt_symbol.split(".")
|
||||||
return symbol, Exchange(exchange_str)
|
return symbol, Exchange(exchange_str)
|
||||||
|
|
||||||
|
|
||||||
def generate_vt_symbol(symbol: str, exchange: Exchange):
|
def generate_vt_symbol(symbol: str, exchange: Exchange):
|
||||||
"""
|
"""
|
||||||
return vt_symbol
|
return vt_symbol
|
||||||
"""
|
"""
|
||||||
return f"{symbol}.{exchange.value}"
|
return f"{symbol}.{exchange.value}"
|
||||||
|
|
||||||
|
|
||||||
def format_number(n):
|
def format_number(n):
|
||||||
"""格式化数字到字符串"""
|
"""格式化数字到字符串"""
|
||||||
rn = round(n, 2) # 保留两位小数
|
rn = round(n, 2) # 保留两位小数
|
||||||
return format(rn, ',') # 加上千分符
|
return format(rn, ',') # 加上千分符
|
||||||
|
|
||||||
|
|
||||||
def _get_trader_dir(temp_name: str):
|
def _get_trader_dir(temp_name: str):
|
||||||
"""
|
"""
|
||||||
Get path where trader is running in.
|
Get path where trader is running in.
|
||||||
@ -212,8 +215,9 @@ def _get_trader_dir(temp_name: str):
|
|||||||
|
|
||||||
|
|
||||||
TRADER_DIR, TEMP_DIR = _get_trader_dir(".vntrader")
|
TRADER_DIR, TEMP_DIR = _get_trader_dir(".vntrader")
|
||||||
sys.path.append(str(TRADER_DIR))
|
if TRADER_DIR not in sys.path:
|
||||||
print(f'sys.path append: {str(TRADER_DIR)}')
|
sys.path.append(str(TRADER_DIR))
|
||||||
|
print(f'sys.path append: {str(TRADER_DIR)}')
|
||||||
|
|
||||||
|
|
||||||
def get_file_path(filename: str):
|
def get_file_path(filename: str):
|
||||||
@ -306,7 +310,7 @@ def print_dict(d: dict):
|
|||||||
return '\n'.join([f'{key}:{d[key]}' for key in sorted(d.keys())])
|
return '\n'.join([f'{key}:{d[key]}' for key in sorted(d.keys())])
|
||||||
|
|
||||||
|
|
||||||
def append_data(self, file_name: str, dict_data: dict, field_names: list = []):
|
def append_data(file_name: str, dict_data: dict, field_names: list = []):
|
||||||
"""
|
"""
|
||||||
添加数据到csv文件中
|
添加数据到csv文件中
|
||||||
:param file_name: csv的文件全路径
|
:param file_name: csv的文件全路径
|
||||||
@ -360,19 +364,254 @@ def import_module_by_str(import_module_name):
|
|||||||
|
|
||||||
comp = modules[-1]
|
comp = modules[-1]
|
||||||
if not hasattr(mod, comp):
|
if not hasattr(mod, comp):
|
||||||
loaded_modules = '.'.join([loaded_modules,comp])
|
loaded_modules = '.'.join([loaded_modules, comp])
|
||||||
print('realod {}'.format(loaded_modules))
|
print('realod {}'.format(loaded_modules))
|
||||||
mod = reload(loaded_modules)
|
mod = reload(loaded_modules)
|
||||||
else:
|
else:
|
||||||
print('from {} import {}'.format(loaded_modules,comp))
|
print('from {} import {}'.format(loaded_modules, comp))
|
||||||
mod = getattr(mod, comp)
|
mod = getattr(mod, comp)
|
||||||
return mod
|
return mod
|
||||||
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print('import {} fail,{},{}'.format(import_module_name,str(ex),traceback.format_exc()))
|
print('import {} fail,{},{}'.format(import_module_name, str(ex), traceback.format_exc()))
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def save_df_to_excel(file_name, sheet_name, df):
|
||||||
|
"""
|
||||||
|
保存dataframe到execl
|
||||||
|
:param file_name: 保存的excel文件名
|
||||||
|
:param sheet_name: 保存的sheet
|
||||||
|
:param df: dataframe
|
||||||
|
:return: True/False
|
||||||
|
"""
|
||||||
|
if file_name is None or sheet_name is None or df is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# ----------------------------- 扩展的功能 ---------
|
||||||
|
try:
|
||||||
|
import openpyxl
|
||||||
|
from openpyxl.utils.dataframe import dataframe_to_rows
|
||||||
|
# from openpyxl.drawing.image import Image
|
||||||
|
except: # noqa
|
||||||
|
print(u'can not import openpyxl', file=sys.stderr)
|
||||||
|
|
||||||
|
if 'openpyxl' not in sys.modules:
|
||||||
|
print(u'can not import openpyxl', file=sys.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
ws = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 读取文件
|
||||||
|
wb = openpyxl.load_workbook(file_name)
|
||||||
|
except: # noqa
|
||||||
|
# 创建一个excel workbook
|
||||||
|
wb = openpyxl.Workbook()
|
||||||
|
ws = wb.active
|
||||||
|
ws.title = sheet_name
|
||||||
|
try:
|
||||||
|
# 定位WorkSheet
|
||||||
|
if ws is None:
|
||||||
|
ws = wb[sheet_name]
|
||||||
|
except: # noqa
|
||||||
|
# 创建一个WorkSheet
|
||||||
|
ws = wb.create_sheet()
|
||||||
|
ws.title = sheet_name
|
||||||
|
|
||||||
|
rows = dataframe_to_rows(df)
|
||||||
|
for r_idx, row in enumerate(rows, 1):
|
||||||
|
for c_idx, value in enumerate(row, 1):
|
||||||
|
ws.cell(row=r_idx, column=c_idx, value=value)
|
||||||
|
|
||||||
|
# Save the workbook
|
||||||
|
wb.save(file_name)
|
||||||
|
wb.close()
|
||||||
|
except Exception as ex:
|
||||||
|
import traceback
|
||||||
|
print(u'save_df_to_excel exception:{}'.format(str(ex)), traceback.format_exc(), file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
def save_text_to_excel(file_name, sheet_name, text):
|
||||||
|
"""
|
||||||
|
保存文本文件到excel
|
||||||
|
:param file_name:
|
||||||
|
:param sheet_name:
|
||||||
|
:param text:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if file_name is None or len(sheet_name) == 0 or len(text) == 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# ----------------------------- 扩展的功能 ---------
|
||||||
|
try:
|
||||||
|
import openpyxl
|
||||||
|
# from openpyxl.utils.dataframe import dataframe_to_rows
|
||||||
|
# from openpyxl.drawing.image import Image
|
||||||
|
except: # noqa
|
||||||
|
print(u'can not import openpyxl', file=sys.stderr)
|
||||||
|
|
||||||
|
if 'openpyxl' not in sys.modules:
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
ws = None
|
||||||
|
try:
|
||||||
|
# 读取文件
|
||||||
|
wb = openpyxl.load_workbook(file_name)
|
||||||
|
except: # noqa
|
||||||
|
# 创建一个excel workbook
|
||||||
|
wb = openpyxl.Workbook()
|
||||||
|
ws = wb.active
|
||||||
|
ws.title = sheet_name
|
||||||
|
try:
|
||||||
|
# 定位WorkSheet
|
||||||
|
if ws is None:
|
||||||
|
ws = wb[sheet_name]
|
||||||
|
except: # noqa
|
||||||
|
# 创建一个WorkSheet
|
||||||
|
ws = wb.create_sheet()
|
||||||
|
ws.title = sheet_name
|
||||||
|
|
||||||
|
# 设置宽度,自动换行方式
|
||||||
|
ws.column_dimensions["A"].width = 120
|
||||||
|
ws['A2'].alignment = openpyxl.styles.Alignment(wrapText=True)
|
||||||
|
ws['A2'].value = text
|
||||||
|
|
||||||
|
# Save the workbook
|
||||||
|
wb.save(file_name)
|
||||||
|
wb.close()
|
||||||
|
return True
|
||||||
|
except Exception as ex:
|
||||||
|
import traceback
|
||||||
|
print(u'save_text_to_excel exception:{}'.format(str(ex)), traceback.format_exc(), file=sys.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def save_images_to_excel(file_name, sheet_name, image_names):
|
||||||
|
"""
|
||||||
|
# 保存图形文件到excel
|
||||||
|
:param file_name: excel文件名
|
||||||
|
:param sheet_name: workSheet
|
||||||
|
:param image_names: 图像文件名列表
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if file_name is None or len(sheet_name) == 0 or len(image_names) == 0:
|
||||||
|
return False
|
||||||
|
# ----------------------------- 扩展的功能 ---------
|
||||||
|
try:
|
||||||
|
import openpyxl
|
||||||
|
# from openpyxl.utils.dataframe import dataframe_to_rows
|
||||||
|
from openpyxl.drawing.image import Image
|
||||||
|
except Exception as ex:
|
||||||
|
print(f'can not import openpyxl:{str(ex)}', file=sys.stderr)
|
||||||
|
|
||||||
|
if 'openpyxl' not in sys.modules:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
ws = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 读取文件
|
||||||
|
wb = openpyxl.load_workbook(file_name)
|
||||||
|
except: # noqa
|
||||||
|
# 创建一个excel workbook
|
||||||
|
wb = openpyxl.Workbook()
|
||||||
|
ws = wb.active
|
||||||
|
ws.title = sheet_name
|
||||||
|
try:
|
||||||
|
# 定位WorkSheet
|
||||||
|
if ws is None:
|
||||||
|
ws = wb[sheet_name]
|
||||||
|
except Exception as ex: # noqa
|
||||||
|
# 创建一个WorkSheet
|
||||||
|
ws = wb.create_sheet()
|
||||||
|
ws.title = sheet_name
|
||||||
|
|
||||||
|
i = 1
|
||||||
|
|
||||||
|
for image_name in image_names:
|
||||||
|
try:
|
||||||
|
# 加载图形文件
|
||||||
|
img1 = Image(image_name)
|
||||||
|
|
||||||
|
cell_id = 'A{0}'.format(i)
|
||||||
|
ws[cell_id].value = image_name
|
||||||
|
cell_id = 'A{0}'.format(i + 1)
|
||||||
|
|
||||||
|
i += 30
|
||||||
|
|
||||||
|
# 添加至对应的WorkSheet中
|
||||||
|
ws.add_image(img1, cell_id)
|
||||||
|
except Exception as ex:
|
||||||
|
print('exception loading image {}, {}'.format(image_name, str(ex)), file=sys.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Save the workbook
|
||||||
|
wb.save(file_name)
|
||||||
|
wb.close()
|
||||||
|
return True
|
||||||
|
except Exception as ex:
|
||||||
|
import traceback
|
||||||
|
print(u'save_images_to_excel exception:{}'.format(str(ex)), traceback.format_exc(), file=sys.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def display_dual_axis(df, columns1, columns2=[], invert_yaxis1=False, invert_yaxis2=False, file_name=None,
|
||||||
|
sheet_name=None,
|
||||||
|
image_name=None):
|
||||||
|
"""
|
||||||
|
显示(保存)双Y轴的走势图
|
||||||
|
:param df: DataFrame
|
||||||
|
:param columns1: y1轴
|
||||||
|
:param columns2: Y2轴
|
||||||
|
:param invert_yaxis1: Y1 轴反转
|
||||||
|
:param invert_yaxis2: Y2 轴翻转
|
||||||
|
:param file_name: 保存的excel 文件名称
|
||||||
|
:param sheet_name: excel 的sheet
|
||||||
|
:param image_name: 保存的image 文件名
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
import matplotlib
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
matplotlib.rcParams['figure.figsize'] = (20.0, 10.0)
|
||||||
|
|
||||||
|
df1 = df[columns1]
|
||||||
|
df1.index = list(range(len(df)))
|
||||||
|
fig, ax1 = plt.subplots()
|
||||||
|
if invert_yaxis1:
|
||||||
|
ax1.invert_yaxis()
|
||||||
|
ax1.plot(df1)
|
||||||
|
|
||||||
|
if len(columns2) > 0:
|
||||||
|
df2 = df[columns2]
|
||||||
|
df2.index = list(range(len(df)))
|
||||||
|
ax2 = ax1.twinx()
|
||||||
|
if invert_yaxis2:
|
||||||
|
ax2.invert_yaxis()
|
||||||
|
ax2.plot(df2)
|
||||||
|
|
||||||
|
# 修改x轴得label为时间
|
||||||
|
xt = ax1.get_xticks()
|
||||||
|
xt2 = [df.index[int(i)] for i in xt[1:-2]]
|
||||||
|
xt2.insert(0, '')
|
||||||
|
xt2.append('')
|
||||||
|
ax1.set_xticklabels(xt2)
|
||||||
|
|
||||||
|
# 是否保存图片到文件
|
||||||
|
if image_name is not None:
|
||||||
|
fig = plt.gcf()
|
||||||
|
fig.savefig(image_name, bbox_inches='tight')
|
||||||
|
|
||||||
|
# 插入图片到指定的excel文件sheet中并保存excel
|
||||||
|
if file_name is not None and sheet_name is not None:
|
||||||
|
save_images_to_excel(file_name, sheet_name, [image_name])
|
||||||
|
else:
|
||||||
|
plt.show()
|
||||||
|
|
||||||
class BarGenerator:
|
class BarGenerator:
|
||||||
"""
|
"""
|
||||||
For:
|
For:
|
||||||
|
Loading…
Reference in New Issue
Block a user