[bug fix]

This commit is contained in:
msincenselee 2020-05-08 10:15:05 +08:00
parent 5580336f4b
commit b94065902a
11 changed files with 268 additions and 68 deletions

View File

@ -83,6 +83,20 @@
- 支持bar方式回测/组合回测 - 支持bar方式回测/组合回测
- 增强期货交易模板 - 增强期货交易模板
13、 增加App: cta_stock, 包括:
- 增加baostock数据源可下载股票基本信息复权因子非复权5Min数据k线满足大部分Cta策略的回测了。
- 使用tdx的历史逐笔成交数据可缓存每日数据=>pkb2文件支持tick回测。
- 独立的CTA引擎 cta_stock运行股票CTA策略时替代原版cta_strategy引擎
- 提供股票专用模板,支持目标股票买入卖出,市场盘面算法交易,支持策略多股票持久化
- 支持策略中获取账号资金/可用余额/当前仓位/风控仓位
- 支持策略中获取账号所有股票持仓
- 支持bar/tick方式回测/组合回测
- 支持可转债日内交易回测,支持动态前复权。
- 支持盘前复权信息事件【待更新】
大佳 大佳
QQ/Wechat28888502 QQ/Wechat28888502

View File

@ -327,7 +327,7 @@ class AccountRecorder(BaseEngine):
def update_order(self, event: Event): def update_order(self, event: Event):
"""更新当日记录""" """更新当日记录"""
order = event.data order = event.data
self.write_log(u'记录委托日志:{}'.format(order.__dict__)) # self.write_log(u'记录委托日志:{}'.format(order.__dict__))
if len(order.sys_orderid) == 0: if len(order.sys_orderid) == 0:
# 未有系统的委托编号,不做持久化 # 未有系统的委托编号,不做持久化
return return
@ -441,7 +441,7 @@ class AccountRecorder(BaseEngine):
def update_strategy_snapshot(self, event: Event): def update_strategy_snapshot(self, event: Event):
"""更新策略切片""" """更新策略切片"""
snapshot = event.data snapshot = event.data
self.write_log(f"保存切片,{snapshot.get('account_id')},策略:{snapshot.get('strategy')}") # self.write_log(f"保存切片,{snapshot.get('account_id')},策略:{snapshot.get('strategy')}")
klines = snapshot.pop('klines', None) klines = snapshot.pop('klines', None)
if klines: if klines:
self.write_log(f"转换 =>BSON.binary.Binary") self.write_log(f"转换 =>BSON.binary.Binary")

View File

@ -5,7 +5,7 @@ Defines constants and objects used in CtaCrypto App.
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 vnpy.trader.constant import Direction, Offset, Interval from vnpy.trader.constant import Direction, Offset, Interval, Exchange
APP_NAME = "CtaStock" APP_NAME = "CtaStock"
STOPORDER_PREFIX = "STOP" STOPORDER_PREFIX = "STOP"

View File

@ -1,5 +1,5 @@
""" """
数字货币CTA策略运行引擎 股票CTA策略运行引擎
华富资产 华富资产
""" """
@ -154,7 +154,7 @@ class CtaEngine(BaseEngine):
self.load_strategy_class() self.load_strategy_class()
self.load_strategy_setting() self.load_strategy_setting()
self.write_log("CTA策略数字货币引擎初始化成功") self.write_log("CTA策略股票引擎初始化成功")
def close(self): def close(self):
"""停止所属有的策略""" """停止所属有的策略"""
@ -218,6 +218,7 @@ class CtaEngine(BaseEngine):
# 主动获取所有策略得持仓信息 # 主动获取所有策略得持仓信息
all_strategy_pos = self.get_all_strategy_pos() all_strategy_pos = self.get_all_strategy_pos()
if dt.minute % 5 == 0:
# 比对仓位,使用上述获取得持仓信息,不用重复获取 # 比对仓位,使用上述获取得持仓信息,不用重复获取
self.compare_pos(strategy_pos_list=copy(all_strategy_pos)) self.compare_pos(strategy_pos_list=copy(all_strategy_pos))
@ -1456,7 +1457,7 @@ class CtaEngine(BaseEngine):
d.update(strategy.get_parameters()) d.update(strategy.get_parameters())
return d return d
def compare_pos(self): def compare_pos(self,strategy_pos_list=[]):
""" """
对比账号&策略的持仓,不同的话则发出微信提醒 对比账号&策略的持仓,不同的话则发出微信提醒
:return: :return:
@ -1468,6 +1469,7 @@ class CtaEngine(BaseEngine):
self.write_log(u'开始对比账号&策略的持仓') self.write_log(u'开始对比账号&策略的持仓')
# 获取当前策略得持仓 # 获取当前策略得持仓
if len(strategy_pos_list) == 0:
strategy_pos_list = self.get_all_strategy_pos() strategy_pos_list = self.get_all_strategy_pos()
self.write_log(u'策略持仓清单:{}'.format(strategy_pos_list)) self.write_log(u'策略持仓清单:{}'.format(strategy_pos_list))
@ -1520,12 +1522,11 @@ class CtaEngine(BaseEngine):
compare_info = '' compare_info = ''
for vt_symbol in sorted(vt_symbols): for vt_symbol in sorted(vt_symbols):
# 发送不一致得结果 # 发送不一致得结果
symbol_pos = compare_pos.pop(vt_symbol) symbol_pos = compare_pos.pop(vt_symbol, {
d_long = { '账号多单': 0,
'account_id': self.engine_config.get('account_id', '-'), '策略多单': 0,
'vt_symbol': vt_symbol, '多单策略': []
'direction': Direction.LONG.value, })
'strategy_list': symbol_pos.get('多单策略', [])}
# 多单都一致 # 多单都一致
if round(symbol_pos['账号多单'], 7) == round(symbol_pos['策略多单'], 7): if round(symbol_pos['账号多单'], 7) == round(symbol_pos['策略多单'], 7):
@ -1596,7 +1597,7 @@ class CtaEngine(BaseEngine):
self.add_strategy( self.add_strategy(
class_name=strategy_config["class_name"], class_name=strategy_config["class_name"],
strategy_name=strategy_name, strategy_name=strategy_name,
vt_symbol=strategy_config["vt_symbol"], vt_symbols=strategy_config["vt_symbols"],
setting=strategy_config["setting"], setting=strategy_config["setting"],
auto_init=strategy_config.get('auto_init', False), auto_init=strategy_config.get('auto_init', False),
auto_start=strategy_config.get('auto_start', False) auto_start=strategy_config.get('auto_start', False)
@ -1612,7 +1613,7 @@ class CtaEngine(BaseEngine):
old_config = self.strategy_setting.get('strategy_name', {}) old_config = self.strategy_setting.get('strategy_name', {})
new_config = { new_config = {
"class_name": strategy.__class__.__name__, "class_name": strategy.__class__.__name__,
"vt_symbol": strategy.vt_symbol, "vt_symbols": strategy.vt_symbols,
"auto_init": auto_init, "auto_init": auto_init,
"auto_start": auto_start, "auto_start": auto_start,
"setting": setting "setting": setting
@ -1631,7 +1632,7 @@ class CtaEngine(BaseEngine):
""" """
if strategy_name not in self.strategy_setting: if strategy_name not in self.strategy_setting:
return return
self.write_log(f'移除CTA数字货币引擎{strategy_name}的配置') self.write_log(f'移除CTA股票引擎{strategy_name}的配置')
self.strategy_setting.pop(strategy_name) self.strategy_setting.pop(strategy_name)
save_json(self.setting_filename, self.strategy_setting) save_json(self.setting_filename, self.strategy_setting)
@ -1698,7 +1699,7 @@ class CtaEngine(BaseEngine):
if strategy: if strategy:
subject = f"{strategy.strategy_name}" subject = f"{strategy.strategy_name}"
else: else:
subject = "CTA策略数字货币引擎" subject = "CTA策略股票引擎"
self.main_engine.send_email(subject, msg) self.main_engine.send_email(subject, msg)
@ -1712,6 +1713,6 @@ class CtaEngine(BaseEngine):
if strategy: if strategy:
subject = f"{strategy.strategy_name}" subject = f"{strategy.strategy_name}"
else: else:
subject = "CTACRYPTO引擎" subject = "CTASTOCK引擎"
send_wx_msg(content=f'{subject}:{msg}') send_wx_msg(content=f'{subject}:{msg}')

View File

@ -142,7 +142,7 @@ class CtaManager(QtWidgets.QWidget):
if n == editor.Accepted: if n == editor.Accepted:
setting = editor.get_setting() setting = editor.get_setting()
vt_symbol = setting.pop("vt_symbol") vt_symbols = setting.pop("vt_symbols").split(",")
strategy_name = setting.pop("strategy_name") strategy_name = setting.pop("strategy_name")
auto_init = setting.pop("auto_init", False) auto_init = setting.pop("auto_init", False)
auto_start = setting.pop("auto_start", False) auto_start = setting.pop("auto_start", False)
@ -206,12 +206,12 @@ class StrategyManager(QtWidgets.QFrame):
save_button.clicked.connect(self.save_strategy) save_button.clicked.connect(self.save_strategy)
strategy_name = self._data["strategy_name"] strategy_name = self._data["strategy_name"]
vt_symbol = self._data["vt_symbol"] #vt_symbol = self._data["vt_symbol"]
class_name = self._data["class_name"] class_name = self._data["class_name"]
author = self._data["author"] author = self._data["author"]
label_text = ( label_text = (
f"{strategy_name} - {vt_symbol} ({class_name} by {author})" f"{strategy_name} - ({class_name} by {author})"
) )
label = QtWidgets.QLabel(label_text) label = QtWidgets.QLabel(label_text)
label.setAlignment(QtCore.Qt.AlignCenter) label.setAlignment(QtCore.Qt.AlignCenter)
@ -411,7 +411,7 @@ class SettingEditor(QtWidgets.QDialog):
if self.class_name: if self.class_name:
self.setWindowTitle(f"添加策略:{self.class_name}") self.setWindowTitle(f"添加策略:{self.class_name}")
button_text = "添加" button_text = "添加"
parameters = {"strategy_name": "", "vt_symbol": "", "auto_init": True, "auto_start": True} parameters = {"strategy_name": "", "vt_symbols": "", "auto_init": True, "auto_start": True}
parameters.update(self.parameters) parameters.update(self.parameters)
else: else:

View File

@ -1158,9 +1158,13 @@ class BackTestingEngine(object):
# 3. 则在实际中的成交价会是100而不是105因为委托发出时市场的最优价格是100 # 3. 则在实际中的成交价会是100而不是105因为委托发出时市场的最优价格是100
if buy_cross: if buy_cross:
trade_price = min(order.price, buy_best_cross_price) trade_price = min(order.price, buy_best_cross_price)
else: else:
trade_price = max(order.price, sell_best_cross_price) trade_price = max(order.price, sell_best_cross_price)
# renko bar较为特殊使用委托价进行成交
if trade.vt_symbol.startswith('future_renko'):
trade_price = order.price
trade.price = trade_price trade.price = trade_price
# 记录该合约来自哪个策略实例 # 记录该合约来自哪个策略实例

View File

@ -85,10 +85,31 @@ class PortfolioTestingEngine(BackTestingEngine):
"date": str, "date": str,
"time": str "time": str
} }
if vt_symbol.startswith('future_renko'):
data_types.update({
"color": str,
"seconds": int,
"high_seconds": int,
"low_seconds": int,
"height": float,
"up_band": float,
"down_band": float,
"low_time": str,
"high_time": str
})
# 加载csv文件 =》 dateframe # 加载csv文件 =》 dateframe
symbol_df = pd.read_csv(bar_file, dtype=data_types) symbol_df = pd.read_csv(bar_file, dtype=data_types)
if len(symbol_df)==0:
self.write_error(f'回测时加载{vt_symbol} csv文件{bar_file}失败。')
return False
first_dt = symbol_df.iloc[0]['datetime']
if '.' in first_dt:
datetime_format = "%Y-%m-%d %H:%M:%S.%f"
else:
datetime_format = "%Y-%m-%d %H:%M:%S"
# 转换时间str =》 datetime # 转换时间str =》 datetime
symbol_df["datetime"] = pd.to_datetime(symbol_df["datetime"], format="%Y-%m-%d %H:%M:%S") symbol_df["datetime"] = pd.to_datetime(symbol_df["datetime"], format=datetime_format)
# 设置时间为索引 # 设置时间为索引
symbol_df = symbol_df.set_index("datetime") symbol_df = symbol_df.set_index("datetime")

View File

@ -12,6 +12,7 @@ import sys
import traceback import traceback
import talib as ta import talib as ta
import numpy as np import numpy as np
import pandas as pd
import csv import csv
from collections import OrderedDict from collections import OrderedDict
@ -49,6 +50,14 @@ def get_cta_bar_type(bar_name: str):
return CtaDayBar, 1 return CtaDayBar, 1
else: else:
return CtaDayBar, int(interval) return CtaDayBar, int(interval)
if bar_name.startswith('W'):
interval = bar_name.replace('W', '')
if len(interval) == 0:
return CtaWeekBar, 1
else:
return CtaWeekBar, int(interval)
raise Exception(u'{}参数错误'.format(bar_name)) raise Exception(u'{}参数错误'.format(bar_name))
@ -298,6 +307,9 @@ class CtaLineBar(object):
self.paramList.append('para_bias2_len') self.paramList.append('para_bias2_len')
self.paramList.append('para_bias3_len') self.paramList.append('para_bias3_len')
self.paramList.append('para_skdj_n')
self.paramList.append('para_skdj_m')
self.paramList.append('para_bd_len') self.paramList.append('para_bd_len')
self.paramList.append('is_7x24') self.paramList.append('is_7x24')
@ -403,6 +415,9 @@ class CtaLineBar(object):
self.para_yb_ref = 1 # 趋势线参照周期 self.para_yb_ref = 1 # 趋势线参照周期
self.para_yb_len = 10 # 趋势线观测周期 self.para_yb_len = 10 # 趋势线观测周期
self.para_skdj_m = 0 # 3 # SKDJ NN
self.para_skdj_n = 0 # 9 # SKDJ MM
self.para_golden_n = 0 # 黄金分割的观测周期一般设置为60或120 self.para_golden_n = 0 # 黄金分割的观测周期一般设置为60或120
self.para_active_area = False # 是否激活区域划分 self.para_active_area = False # 是否激活区域划分
@ -494,12 +509,12 @@ class CtaLineBar(object):
self.line_upper_atan = [] self.line_upper_atan = []
self.line_middle_atan = [] self.line_middle_atan = []
self.line_lower_atan = [] self.line_lower_atan = []
self._rt_upper = 0 self._rt_upper = None
self._rt_middle = 0 self._rt_middle = None
self._rt_lower = 0 self._rt_lower = None
self._rt_upper_atan = 0 self._rt_upper_atan = None
self._rt_middle_atan = 0 self._rt_middle_atan = None
self._rt_lower_atan = 0 self._rt_lower_atan = None
self.cur_upper = 0 # 最后一根K的Boll上轨数值与MinDiff取整 self.cur_upper = 0 # 最后一根K的Boll上轨数值与MinDiff取整
self.cur_middle = 0 # 最后一根K的Boll中轨数值与MinDiff取整 self.cur_middle = 0 # 最后一根K的Boll中轨数值与MinDiff取整
@ -560,13 +575,19 @@ class CtaLineBar(object):
# K 线的CCI计算数据 # K 线的CCI计算数据
self.line_cci = [] self.line_cci = []
self.line_cci_ema = []
self.cur_cci = None self.cur_cci = None
self.cur_cci_ema = None
self._rt_cci = None self._rt_cci = None
self._rt_cci_ema = None
# 卡尔曼过滤器 # 卡尔曼过滤器
self.kf = None self.kf = None
self.line_state_mean = [] # 卡尔曼均线 self.line_state_mean = [] # 卡尔曼均线
self.line_state_upper = [] # 卡尔曼均线+2标准差
self.line_state_lower = [] # 卡尔曼均线-2标准差
self.line_state_covar = [] # 方差 self.line_state_covar = [] # 方差
self.cur_state_std = None
# SAR 抛物线 # SAR 抛物线
self.cur_sar_direction = '' # up/down self.cur_sar_direction = '' # up/down
@ -639,6 +660,12 @@ class CtaLineBar(object):
self._bd_fast = 0 self._bd_fast = 0
self._bd_slow = 0 self._bd_slow = 0
# SKDJ
self.line_skdj_k = []
self.line_skdj_d = []
self.cur_skdj_k = 0
self.cur_skdj_d = 0
def set_params(self, setting: dict = {}): def set_params(self, setting: dict = {}):
"""设置参数""" """设置参数"""
d = self.__dict__ d = self.__dict__
@ -816,6 +843,7 @@ class CtaLineBar(object):
self.__count_area(bar) self.__count_area(bar)
self.__count_bias() self.__count_bias()
self.__count_bd() self.__count_bd()
self.__count_skdj()
self.export_to_csv(bar) self.export_to_csv(bar)
self.rt_executed = False self.rt_executed = False
@ -968,8 +996,8 @@ class CtaLineBar(object):
round(self.line_d[-1], self.round_n), round(self.line_d[-1], self.round_n),
round(self.line_j[-1], self.round_n)) round(self.line_j[-1], self.round_n))
if self.para_cci_len > 0 and len(self.line_cci) > 0: if self.para_cci_len > 0 and len(self.line_cci) > 0 and len(self.line_cci_ema) > 0:
msg = msg + u',Cci({0}):{1}'.format(self.para_cci_len, self.line_cci[-1]) msg = msg + u',Cci({0}):{1}, EMA(Cci):{2}'.format(self.para_cci_len, self.line_cci[-1], self.line_cci_ema[-1])
if (self.para_boll_len > 0 or self.para_boll_tb_len > 0) and len(self.line_boll_upper) > 0: if (self.para_boll_len > 0 or self.para_boll_tb_len > 0) and len(self.line_boll_upper) > 0:
msg = msg + u',Boll({}):std:{},mid:{},up:{},low:{},Atan:[mid:{},up:{},low:{}]'. \ msg = msg + u',Boll({}):std:{},mid:{},up:{},low:{},Atan:[mid:{},up:{},low:{}]'. \
@ -1246,6 +1274,33 @@ class CtaLineBar(object):
del self.line_pre_low[0] del self.line_pre_low[0]
self.line_pre_low.append(preLow) self.line_pre_low.append(preLow)
def get_sar(self, direction, cur_sar, cur_af=0, sar_limit=0.2, sar_step=0.02):
"""
抛物线计算方法
:param direction: Direction
:param cur_sar: 当前抛物线价格
:param cur_af: 当前抛物线价格
:param sar_limit: 最大加速范围
:param sar_step: 加速因子
:return: 新的
"""
if np.isnan(self.high_array[-1]):
return cur_sar, cur_af
# 向上抛物线
if direction == Direction.LONG:
af = min(sar_limit, cur_af + sar_step)
ep = self.high_array[-1]
sar = cur_sar + af * (ep - cur_sar)
return sar, af
# 向下抛物线
elif direction == Direction.SHORT:
af = min(sar_limit, cur_af + sar_step)
ep = self.low_array[-1]
sar = cur_sar + af * (ep - cur_sar)
return sar, af
else:
return cur_sar, cur_af
def __count_sar(self): def __count_sar(self):
"""计算K线的SAR""" """计算K线的SAR"""
@ -1280,19 +1335,28 @@ class CtaLineBar(object):
self.cur_sar_count = 0 self.cur_sar_count = 0
self.line_sar_top.append(self.line_bar[-2].high_price) # SAR的谷顶 self.line_sar_top.append(self.line_bar[-2].high_price) # SAR的谷顶
self.line_sar_buttom.append(self.line_bar[-2].low_price) # SAR的波底 self.line_sar_buttom.append(self.line_bar[-2].low_price) # SAR的波底
# 当前处于上升抛物线
elif len(self.line_sar_sr_up) > 0: elif len(self.line_sar_sr_up) > 0:
# # K线low仍然在上升抛物线上方延续
if self.low_array[-1] > self.line_sar_sr_up[-1]: if self.low_array[-1] > self.line_sar_sr_up[-1]:
sr0 = self.line_sar_sr_up[-1] sr0 = self.line_sar_sr_up[-1]
ep0 = self.high_array[-1] # 文华使用前一个K线的最高价 ep0 = self.high_array[-1] # 文华使用前一个K线的最高价
af0 = min(self.para_sar_limit, af0 = min(self.para_sar_limit,
self.line_sar_af_up[-1] + self.para_sar_step) # 文华的af随着K线的数目增加而递增没有判断新高 self.line_sar_af_up[-1] + self.para_sar_step) # 文华的af随着K线的数目增加而递增没有判断新高
# 计算出新的抛物线价格
sr = sr0 + af0 * (ep0 - sr0) sr = sr0 + af0 * (ep0 - sr0)
self.line_sar_sr_up.append(sr) self.line_sar_sr_up.append(sr)
self.line_sar_ep_up.append(ep0) self.line_sar_ep_up.append(ep0)
self.line_sar_af_up.append(af0) self.line_sar_af_up.append(af0)
self.line_sar.append(sr) self.line_sar.append(sr)
self.cur_sar_count += 1 self.cur_sar_count += 1
# self.write_log('Up: sr0={},ep0={},af0={},sr={}'.format(sr0, ep0, af0, sr)) # self.write_log('Up: sr0={},ep0={},af0={},sr={}'.format(sr0, ep0, af0, sr))
# K线最低触碰上升的抛物线 =》 转为 下降抛物线
elif self.low_array[-1] <= self.line_sar_sr_up[-1]: elif self.low_array[-1] <= self.line_sar_sr_up[-1]:
ep0 = max(self.high_array[-len(self.line_sar_sr_up):]) ep0 = max(self.high_array[-len(self.line_sar_sr_up):])
sr0 = ep0 sr0 = ep0
@ -2982,17 +3046,46 @@ class CtaLineBar(object):
format(len(self.line_bar), self.para_cci_len + 2)) format(len(self.line_bar), self.para_cci_len + 2))
return return
# 计算第1根RSI曲线 # HIGH = self.high_array[self.high_array > 0]
# LOW = self.low_array[self.low_array > 0]
# CLOSE = self.close_array[self.close_array > 0]
# min_length = min([len(HIGH), len(LOW), len(CLOSE)])
# if min_length < (self.para_cci_len + 10):
# return
# HIGH = HIGH[-min_length:]
# LOW = LOW[-min_length:]
# CLOSE = CLOSE[-min_length:]
# TP = (HIGH + LOW + CLOSE) / 3
cur_cci = ta.CCI(high=self.high_array[-2 * self.para_cci_len:], low=self.low_array[-2 * self.para_cci_len:], TP = self.mid3_array[-self.para_cci_len:]
close=self.close_array[-2 * self.para_cci_len:], timeperiod=self.para_cci_len)[-1] # MA = pd.Series(data=TP).rolling(window=self.para_cci_len).mean().values
# MD = pd.Series(data=(TP - MA)).abs().rolling(window=self.para_cci_len).mean().values
# CCI = (TP - MA) / (0.015 * MD)
MA = np.mean(TP)
MD = np.mean(np.abs(TP - MA))
CCI = (TP[-1] - MA) / (0.015 * MD)
self.cur_cci = round(float(cur_cci), self.round_n) self.cur_cci = round(CCI, self.round_n)
# cur_cci = ta.CCI(high=self.high_array[-2 * self.para_cci_len:], low=self.low_array[-2 * self.para_cci_len:],
# close=self.close_array[-2 * self.para_cci_len:], timeperiod=self.para_cci_len)[-1]
# self.cur_cci = round(float(cur_cci), self.round_n)
if len(self.line_cci) > self.max_hold_bars: if len(self.line_cci) > self.max_hold_bars:
del self.line_cci[0] del self.line_cci[0]
self.line_cci.append(self.cur_cci) self.line_cci.append(self.cur_cci)
if len(self.line_cci) < 30:
self.cur_cci_ema = self.cur_cci
else:
self.cur_cci_ema = self.__ema(self.__ema(self.__ema(self.line_cci[-30:], 3), 2), 2)[-1]
if len(self.line_cci_ema) > self.max_hold_bars:
del self.line_cci_ema[0]
self.line_cci_ema.append(self.cur_cci_ema)
def rt_count_cci(self): def rt_count_cci(self):
"""实时计算CCI值""" """实时计算CCI值"""
if self.para_cci_len <= 0: if self.para_cci_len <= 0:
@ -3004,11 +3097,19 @@ class CtaLineBar(object):
format(len(self.line_bar), self.para_cci_len + 2)) format(len(self.line_bar), self.para_cci_len + 2))
return return
self._rt_cci = ta.CCI(high=np.append(self.high_array[-2 * self.para_cci_len:], [self.line_bar[-1].high_price]), HIGH = np.append(self.high_array[-2 * self.para_cci_len:], [self.line_bar[-1].high_price])
low=np.append(self.low_array[-2 * self.para_cci_len:], [self.line_bar[-1].low_price]), LOW = np.append(self.low_array[-2 * self.para_cci_len:], [self.line_bar[-1].low_price])
close=np.append(self.close_array[-2 * self.para_cci_len:], CLOSE = np.append(self.close_array[-2 * self.para_cci_len:],[self.line_bar[-1].close_price])
[self.line_bar[-1].close_price]), TP = (HIGH + LOW + CLOSE) / 3
timeperiod=self.para_cci_len)[-1] TP = TP[-self.para_cci_len:]
MA = np.mean(TP)
MD = np.mean(np.abs(TP - MA))
CCI = (TP[-1] - MA) / (0.015 * MD)
self._rt_cci = CCI
rt_line_cci = np.append(self.line_cci[-30:], [CCI])
self._rt_cci_ema = self.__ema(self.__ema(self.__ema(rt_line_cci, 3), 2), 2)[-1]
@property @property
def rt_cci(self): def rt_cci(self):
@ -3021,7 +3122,7 @@ class CtaLineBar(object):
"""计算卡尔曼过滤器均线""" """计算卡尔曼过滤器均线"""
if not self.para_active_kf or self.kf is None: if not self.para_active_kf or self.kf is None:
return return
if self.bar_len < 2: if self.bar_len < 26:
return return
if len(self.line_state_mean) == 0 or len(self.line_state_covar) == 0: if len(self.line_state_mean) == 0 or len(self.line_state_covar) == 0:
@ -3035,9 +3136,10 @@ class CtaLineBar(object):
self.write_log(u'导入卡尔曼过滤器失败,需先安装 pip install pykalman') self.write_log(u'导入卡尔曼过滤器失败,需先安装 pip install pykalman')
self.para_active_kf = False self.para_active_kf = False
state_means, state_covariances = self.kf.filter(self.close_array) state_means, state_covariances = self.kf.filter(self.close_array[-1])
m = state_means[-1].item() m = state_means[-1].item()
c = state_covariances[-1].item() c = state_covariances[-1].item()
else: else:
m = self.line_state_mean[-1] m = self.line_state_mean[-1]
c = self.line_state_covar[-1] c = self.line_state_covar[-1]
@ -3047,13 +3149,22 @@ class CtaLineBar(object):
observation=self.close_array[-1]) observation=self.close_array[-1])
m = state_means[-1].item() m = state_means[-1].item()
c = state_covariances[-1].item() c = state_covariances[-1].item()
std_len = 26 if self.bar_len > 26 else self.bar_len
std = np.std(self.close_array[-std_len:], ddof=1)
self.cur_state_std = std
if len(self.line_state_mean) > self.max_hold_bars: if len(self.line_state_mean) > self.max_hold_bars:
del self.line_state_mean[0] del self.line_state_mean[0]
if len(self.line_state_covar) > self.max_hold_bars: if len(self.line_state_covar) > self.max_hold_bars:
del self.line_state_covar[0] del self.line_state_covar[0]
if len(self.line_state_upper) > self.max_hold_bars:
del self.line_state_upper[0]
if len(self.line_state_lower) > self.max_hold_bars:
del self.line_state_lower[0]
self.line_state_upper.append(m + 3 * std)
self.line_state_mean.append(m) self.line_state_mean.append(m)
self.line_state_lower.append(m - 3 * std)
self.line_state_covar.append(c) self.line_state_covar.append(c)
def __count_period(self, bar): def __count_period(self, bar):
@ -4025,6 +4136,15 @@ class CtaLineBar(object):
return self.line_bias3[-1] return self.line_bias3[-1]
return self._rt_bias3 return self._rt_bias3
def __ema(self, data, span):
return pd.Series(data=data).ewm(span=span, adjust=False).mean().values
def __iema(self, this_value, prev_value, span):
return (2 * prev_value + (span - 1) * this_value) / (span + 1)
def __std(self, data, span):
return pd.Series(data=data).rolling(window=span).std().values
def __count_bd(self): def __count_bd(self):
"""计算波段快/慢线""" """计算波段快/慢线"""
# #
@ -4032,18 +4152,25 @@ class CtaLineBar(object):
# 不计算 # 不计算
return return
if len(self.line_bar) < 2 * self.para_bd_len: # 修改 By Huang Jianwei
var2 = self.mid4_array
var2 = var2[var2 > 0]
if len(var2) < (5 * self.para_bd_len):
return return
var3 = self.__ema(var2, self.para_bd_len)
var4 = self.__std(var2, self.para_bd_len)
mid4_ema_array = ta.EMA(self.mid4_array, self.para_bd_len) ## 检查是否有不合理的std
var4_mask = var4 < 1e-5 # 找出不合理的std
var4[var4_mask] = 1e-5 # 用一个小的正数替换
var5 = ((var2 - var3) / var4 * 100 + 200) / 4 # 计算var5
var5[var4_mask] = 0 # 把不合理的std计算的结果抹掉用0填充
# 因为var2-var3是一种类似乖离率的东西长期均值是接近0的所以用0填充有合理性
mid4_std = np.std(self.mid4_array[-self.para_bd_len:], ddof=1) var6 = (self.__ema(var5, 5) - 25) * 1.56
fast_array = self.__ema(var6, 2) * 1.22
mid4_ema_diff_array = self.mid4_array - mid4_ema_array slow_array = self.__ema(fast_array, 2)
var5_array = (mid4_ema_diff_array / mid4_std * 100 + 200) / 4 # 修改完毕
var6_array = (ta.EMA(var5_array, 5) - 25) * 1.56
fast_array = ta.EMA(var6_array, 2) * 1.22
slow_array = ta.EMA(fast_array, 2)
# 快线/慢线最后记录追加到line_bd_fast/ list_bd_slow中 # 快线/慢线最后记录追加到line_bd_fast/ list_bd_slow中
if len(self.line_bd_fast) > self.max_hold_bars: if len(self.line_bd_fast) > self.max_hold_bars:
@ -4069,27 +4196,29 @@ class CtaLineBar(object):
# 不计算 # 不计算
return return
if len(self.line_bar) < 2 * self.para_bd_len: var2 = self.close_array[self.close_array>0]
if len(var2) < (5 * self.para_bd_len):
return return
bar_mid4 = (self.line_bar[-1].close_price * 2 + self.line_bar[-1].high_price + self.line_bar[-1].low_price)/4 bar_mid4 = (self.line_bar[-1].close_price * 2 + self.line_bar[-1].high_price + self.line_bar[-1].low_price)/4
bar_mid4 = round(bar_mid4, self.round_n) bar_mid4 = round(bar_mid4, self.round_n)
mid4_array = np.append(self.mid4_array, [bar_mid4]) mid4_array = np.append(self.mid4_array, [bar_mid4])
mid4_ema_array = ta.EMA(mid4_array, self.para_bd_len) mid4_ema_array = self.__ema(mid4_array, self.para_bd_len)
mid4_std = np.std(mid4_array[-self.para_bd_len:], ddof=1) mid4_std = self.__std(mid4_array, self.para_bd_len)
mid4_ema_diff_array = mid4_array - mid4_ema_array mid4_ema_diff_array = mid4_array - mid4_ema_array
var5_array = (mid4_ema_diff_array / mid4_std * 100 + 200) / 4 var5_array = (mid4_ema_diff_array / mid4_std * 100 + 200) / 4
var6_array = (ta.EMA(var5_array, 5) - 25) * 1.56 var6_array = (self.__ema(var5_array, 5) - 25) * 1.56
fast_array = ta.EMA(var6_array, 2) * 1.22 fast_array = self.__ema(var6_array, 2) * 1.22
slow_array = ta.EMA(fast_array, 2) slow_array = self.__ema(fast_array, 2)
self._bd_fast = fast_array[-1] self._bd_fast = fast_array[-1]
self._bd_slow = slow_array[-1] self._bd_slow = slow_array[-1]
@property @property
def rt_bd_fast(self): def rt_bd_fast(self):
"""波段快线(实时值)"""
self.check_rt_funcs(self.rt_count_bd) self.check_rt_funcs(self.rt_count_bd)
if self._bd_fast is None and len(self.para_bd_len) > 0: if self._bd_fast is None and len(self.para_bd_len) > 0:
return self.line_bd_fast[-1] return self.line_bd_fast[-1]
@ -4097,12 +4226,46 @@ class CtaLineBar(object):
@property @property
def rt_bd_slow(self): def rt_bd_slow(self):
"""波段慢线(实时值)"""
self.check_rt_funcs(self.rt_count_bd) self.check_rt_funcs(self.rt_count_bd)
if self._bd_slow is None and len(self.para_bd_len) > 0: if self._bd_slow is None and len(self.para_bd_len) > 0:
return self.line_bd_slow[-1] return self.line_bd_slow[-1]
return self._bd_slow return self._bd_slow
def __count_skdj(self):
"""计算波段快/慢线"""
#
if self.para_skdj_m <= 0 or self.para_skdj_n <= 0:
# 不计算
return
if len(self.line_bar) < 5 * min(self.para_skdj_m, self.para_skdj_n):
return
NN = self.para_skdj_n
MM = self.para_skdj_m
LOWV = pd.Series(data=self.low_array).rolling(window=NN).min().values
HIGHV = pd.Series(data=self.high_array).rolling(window=NN).max().values
CLOSE = self.close_array
RSV = self.__ema((CLOSE-LOWV)/(HIGHV-LOWV)*100, MM)
K = self.__ema(RSV, MM)
D = pd.Series(data=K).rolling(window=MM).mean().values
if len(self.line_skdj_k) > self.max_hold_bars:
self.line_skdj_k.pop(0)
if not np.isnan(K[-1]):
self.line_skdj_k.append(K[-1])
self.cur_skdj_k = K[-1]
if len(self.line_skdj_d) > self.max_hold_bars:
self.line_skdj_d.pop(0)
if not np.isnan(D[-1]):
self.line_skdj_d.append(D[-1])
self.cur_skdj_d = D[-1]
def write_log(self, content): def write_log(self, content):
"""记录CTA日志""" """记录CTA日志"""
self.strategy.write_log(u'[' + self.name + u']' + content) self.strategy.write_log(u'[' + self.name + u']' + content)

View File

@ -75,7 +75,6 @@ class TdxStockData(object):
self.strategy = strategy self.strategy = strategy
self.best_ip = None self.best_ip = None
self.symbol_exchange_dict = {} # tdx合约与vn交易所的字典
self.symbol_market_dict = {} # tdx合约与tdx市场的字典 self.symbol_market_dict = {} # tdx合约与tdx市场的字典
self.config = get_cache_config(TDX_STOCK_CONFIG) self.config = get_cache_config(TDX_STOCK_CONFIG)
@ -211,12 +210,10 @@ class TdxStockData(object):
tdx_code, market_str = symbol.split('.') tdx_code, market_str = symbol.split('.')
# 1, 上交所 0 深交所 # 1, 上交所 0 深交所
market_id = 1 if market_str.upper() in ['XSHG', Exchange.SSE.value] else 0 market_id = 1 if market_str.upper() in ['XSHG', Exchange.SSE.value] else 0
self.symbol_exchange_dict.update({tdx_code: symbol}) # tdx合约与vn交易所的字典
self.symbol_market_dict.update({tdx_code: market_id}) # tdx合约与tdx市场的字典 self.symbol_market_dict.update({tdx_code: market_id}) # tdx合约与tdx市场的字典
else: else:
market_id = get_tdx_market_code(symbol) market_id = get_tdx_market_code(symbol)
tdx_code = symbol tdx_code = symbol
self.symbol_exchange_dict.update({symbol: symbol}) # tdx合约与vn交易所的字典
self.symbol_market_dict.update({symbol: market_id}) # tdx合约与tdx市场的字典 self.symbol_market_dict.update({symbol: market_id}) # tdx合约与tdx市场的字典
name = self.get_name(tdx_code, market_id) name = self.get_name(tdx_code, market_id)
@ -416,12 +413,10 @@ class TdxStockData(object):
if '.' in symbol: if '.' in symbol:
tdx_code, market_str = symbol.split('.') tdx_code, market_str = symbol.split('.')
market_code = 1 if market_str.upper() in ['XSHG', Exchange.SSE.value] else 0 market_code = 1 if market_str.upper() in ['XSHG', Exchange.SSE.value] else 0
self.symbol_exchange_dict.update({tdx_code: symbol}) # tdx合约与vn交易所的字典
self.symbol_market_dict.update({tdx_code: market_code}) # tdx合约与tdx市场的字典 self.symbol_market_dict.update({tdx_code: market_code}) # tdx合约与tdx市场的字典
else: else:
market_code = get_tdx_market_code(symbol) market_code = get_tdx_market_code(symbol)
tdx_code = symbol tdx_code = symbol
self.symbol_exchange_dict.update({symbol: symbol}) # tdx合约与vn交易所的字典
self.symbol_market_dict.update({symbol: market_code}) # tdx合约与tdx市场的字典 self.symbol_market_dict.update({symbol: market_code}) # tdx合约与tdx市场的字典
symbol_config = self.symbol_dict.get(f'{tdx_code}_{market_code}', {}) symbol_config = self.symbol_dict.get(f'{tdx_code}_{market_code}', {})

View File

@ -562,8 +562,8 @@ class BinancefRestApi(RestClient):
gateway_name=self.gateway_name, gateway_name=self.gateway_name,
) )
self.gateway.on_position(position) self.gateway.on_position(position)
if position.symbol == 'BTCUSDT': #if position.symbol == 'BTCUSDT':
self.gateway.write_log(f'{position.__dict__}\n {d}') # self.gateway.write_log(f'{position.__dict__}\n {d}')
# self.gateway.write_log("持仓信息查询成功") # self.gateway.write_log("持仓信息查询成功")
def on_query_order(self, data: dict, request: Request) -> None: def on_query_order(self, data: dict, request: Request) -> None:

View File

@ -33,6 +33,8 @@ SETTINGS: Dict[str, Any] = {
"database.user": "root", "database.user": "root",
"database.password": "", "database.password": "",
"database.authentication_source": "admin", # for mongodb "database.authentication_source": "admin", # for mongodb
"huafu.data_source": "" # 华富资产自建数据源
} }
# Load global setting from json file. # Load global setting from json file.