Skip to content

Commit

Permalink
add new strategy from th666@joinquant & optimize select engine
Browse files Browse the repository at this point in the history
  • Loading branch information
moyuanz committed Nov 19, 2018
1 parent b6ea69c commit fae8dda
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 3 deletions.
1 change: 1 addition & 0 deletions DevilYuan.pyproj
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@
<Compile Include="Stock\Select\Strategy\Cta\DySS_SwingTrough.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="Stock\Select\Strategy\Cta\DySS_Th666.py" />
<Compile Include="Stock\Select\Strategy\Other\DySS_ETF.py">
<SubType>Code</SubType>
</Compile>
Expand Down
48 changes: 47 additions & 1 deletion DyCommon/DyTalib.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import numpy as np
import pandas as pd

"""
Defaultly, in/out paramters are numpy array.
"""


DyTalibExpAdjust = True # pandas默认是True,同花顺默认是False。True时,最近的值权重会大些。数据足够多的时候,趋向一致。
Expand Down Expand Up @@ -148,4 +153,45 @@ def WATR(highs, lows, closes, W, timeperiod=14):

atr.insert(0, np.nan)

return atr
return atr

def HHV(v, N):
"""
@v: numpy array or list
@return: numpy array
"""
return pd.Series(v).rolling(N).max().values


def LLV(v, N):
"""
@v: numpy array or list
@return: numpy array
"""
return pd.Series(v).rolling(N).min().values

def KDJ(highs, lows, closes, N=9, M1=3, M2=3, adjust=DyTalibExpAdjust):
"""
@closes, highs, lows: numpy array
@return: numpy array
"""
C = closes
H = highs
L = lows

RSV = (C - LLV(L, N)) / (HHV(H, N) - LLV(L, N)) * 100
RSV = RSV[~np.isnan(RSV)]
K = SMA(RSV, N, M1, adjust=adjust)
D = SMA(K, N, M2, adjust=adjust)
J = 3 * np.array(K) - 2 * np.array(D)
return K, D, J

def SINGLE_GOLDEN_CROSS(A, B):
if A[-2] < B[-2] and A[-1] > B[-1]:
return True

return False

def GOLDEN_CROSS(A, B):
var = np.where(A < B, 1, 0)
return (pd.Series(var).diff() < 0).values
15 changes: 13 additions & 2 deletions Stock/Select/Engine/DyStockSelectSelectEngine.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import traceback

from ...Data.Engine.DyStockDataEngine import *
from EventEngine.DyEvent import *
from ..DyStockSelectCommon import *
Expand Down Expand Up @@ -209,7 +211,7 @@ def _runDaysLoop(self):
self._info.print("开始运行日线数据...")

# init progress
self._progress.init(len(self._daysEngine.stockAllCodes), 100, 5)
self._progress.init(len(self._daysEngine.stockAllCodesFunds), 100, 5)

# index loop
for index in self._daysEngine.stockIndexes:
Expand All @@ -219,6 +221,14 @@ def _runDaysLoop(self):

self._progress.update()

# ETF loop
for etfCode in self._daysEngine.stockFunds:
df = self._daysEngine.getDataFrame(etfCode, self._startDay, self._endDay)
if df is not None:
self._strategy.onEtfDays(etfCode, df)

self._progress.update()

# stock loop
for code in self._daysEngine.stockCodes:
df = self._daysEngine.getDataFrame(code, self._startDay, self._endDay)
Expand Down Expand Up @@ -282,7 +292,8 @@ def _run(self):
try:
self._strategy.onInit(self._dataEngine, self._errorDataEngine)
except Exception as ex:
self._info.print('策略onInit异常', DyLogData.error)
traceback.print_exc()
self._info.print('策略onInit异常: {}'.format(ex), DyLogData.error)
return False

# run loop
Expand Down
172 changes: 172 additions & 0 deletions Stock/Select/Strategy/Cta/DySS_Th666.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import operator

from ..DyStockSelectStrategyTemplate import *
from ....Data.Utility.DyStockDataUtility import *
from DyCommon.DyTalib import *


class DySS_Th666(DyStockSelectStrategyTemplate):
"""
https://www.joinquant.com/post/15297?tag=algorithm
1.首先趋势判断,中证500趋势判断,日线级别kdj金叉多区间,上一交易日为大阴线3/5实体(并且50日atr大于2/3平均值),obos(超买超卖指标)小于-100,满足条件则选股
2.选股9:25-9:30之间高开五个点以上(包含),不含st,开盘前五秒上涨,选出并打印
魔元注:
由于obos是绝对值,是不是可以改成相对值? 即 OBOS=(N日内上涨家数移动总和-N日内下跌家数移动总和)/N日内上涨下跌家数总和 * 100
OBOS=N日内上涨家数移动总和-N日内下跌家数移动总和
N日的采样统计一般设定为10日。
"""
name = 'DySS_Th666'
chName = 'th666聚宽求助'

autoFillDays = True
optimizeAutoFillDays = True
continuousTicks = True

colNames = ['代码', '名称']

param = OrderedDict\
([
('基准日期', '2018-11-15'),
('中证500: 上一交易日阴线实体比例>=', 0.1), # 0.6
('中证500: 50日ATR/平均值>', 0.67),
('中证500: OBOS<', 1000), # -100
])


def __init__(self, param, info):
super().__init__(param, info)

# unpack parameters
self._baseDate = param['基准日期']
self._bodyRatio = param['中证500: 上一交易日阴线实体比例>=']
self._atrOverMean = param['中证500: 50日ATR/平均值>']
self._obos = param['中证500: OBOS<']

self._canContinue = True
self._preCloses = {}

def onDaysLoad(self):
return self._baseDate, -50-14

def onTicksLoad(self):
return self._baseDate, 0

def _get500(self, daysEngine):
# 选取中小创流通市值前500只
codes = {}
for code in daysEngine.stockCodes:
# 由于DY里没有中证500成分股信息,所以用中小创代替
indexCode = DyStockCommon.getIndex(code)
if indexCode not in [DyStockCommon.cybIndex, DyStockCommon.zxbIndex]:
continue

df = self._daysEngine.getDataFrame(code)
if df is None:
continue

codes[code] = df['amt'][-1]/df['turn'][-1]

sortedCodes = sorted(codes, key=lambda k: codes[k], reverse=True)
return sortedCodes[:500]

def onInit(self, dataEngine, errorDataEngine):
print("onInit...")

self._daysEngine = dataEngine.daysEngine
self._stockAllCodes = self._daysEngine.stockAllCodes

selectedCodes = self._get500(self._daysEngine)

# obos(超买超卖指标)小于-100
N = 10

# date range
df = self._daysEngine.getDataFrame(DyStockCommon.szIndex)
dateRange = df[:self._baseDate].tail(N+1).index
startDate, endDate = dateRange[0].strftime("%Y-%m-%d"), dateRange[-1].strftime("%Y-%m-%d")

zeros = pd.Series([0]*N, index=df[:self._baseDate].tail(N).index)
ups = zeros
downs = zeros
for code in selectedCodes:
df = self._daysEngine.getDataFrame(code)
if df is None:
continue

closes = df['close'][startDate:endDate]
pctChanges = closes.pct_change().dropna()

# 上涨
temp = zeros + pctChanges.apply(lambda x: 1 if x > 0 else 0)
ups = ups + temp.fillna(0)

# 下跌
temp = zeros + pctChanges.apply(lambda x: 1 if x < 0 else 0)
downs = downs + temp.fillna(0)

obos = ups.sum() - downs.sum()
if obos >= self._obos:
print("中证500: OBOS: {}".format(obos))
self._canContinue = False

def onEtfDays(self, code, df):
""" Etf日线数据 """
if not self._canContinue:
return

# 日线级别kdj金叉多区间
if code == DyStockCommon.etf500:
K, D, J = KDJ(df['high'].values, df['low'].values, df['close'].values, adjust=False)
if K[-1] < D[-1]:
print("中证500: KDJ不是金叉区间")
self._canContinue = False
return

# 上一交易日为大阴线3/5实体
if df['close'][-2] >= df['open'][-2]:
print("中证500: 上一交易日不是阴线")
self._canContinue = False
return

bodyRatio = abs(df['close'][-2] - df['open'][-2])/abs(df['low'][-2] - df['high'][-2])
if bodyRatio < self._bodyRatio:
print("中证500: 上一交易日阴线实体比例: {}".format(bodyRatio))
self._canContinue = False
return

# 50日atr大于2/3平均值
atr = np.array(ATR(df['high'].values, df['low'].values, df['close'].values))
atrOverMean = float(atr[-1]/atr[-50:].mean())
if atrOverMean <= self._atrOverMean:
print("中证500: 50日atr/平均值: {}".format(atrOverMean))
self._canContinue = False
return

def onStockDays(self, code, df):
if not self._canContinue:
return

# 选股9:25-9:30之间高开五个点以上(包含),不含st
openIncrease = (df['open'][-1] - df['close'][-2])/df['close'][-2] * 100
if openIncrease < 5:
return

if 'st' in self._stockAllCodes[code] or 'ST' in self._stockAllCodes[code]:
return

self._preCloses[code] = df['close'][-2]

# 设置结果
row = [code, self._stockAllCodes[code]]
print(row)
self._result.append(row)

def onStockTicks(self, code, dfs):
# 开盘前五秒上涨
prices = dfs['price']
date = dfs.index[0].strftime("%Y-%m-%d")
if self._preCloses[code] >= prices[:"{} 09:30:05".format(date)][-1]:
print("Remove {}".format([code, self._stockAllCodes[code]]))
self.removeFromResult(code)
4 changes: 4 additions & 0 deletions Stock/Select/Strategy/DyStockSelectStrategyTemplate.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ def onIndexDays(self, code, df):
""" 指数日线数据 """
pass

def onEtfDays(self, code, df):
""" ETF日线数据 """
pass

def onStockDays(self, code, df):
""" 个股日线数据 """
pass
Expand Down

0 comments on commit fae8dda

Please sign in to comment.