Commit 3de16f67 by 周帅

initial commit

parents
from KYBT.BT import engine2
from KYBT.util.kySDK import *
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.CRITICAL)
import time
from KYBT.BT.evaluator import evaluator
# ['600000','000002']
#用户输入
# "stockPicker":{'pick':True,'dateList':['2014-07-01','2014-07-05']},
# "stockPicker":{'pick':False,'dateList':None},
inputJson = \
{ "strategyID":'01',
"stockPicker":{'pick':False,'dateList':None},
"criteriaDict" : {'存货周转率':['最近一年',None,None],'主营收入同比':['最近一年',None,None],'流动资产':['最近一期',None,None]},
"portforlio": 'ZZ500',
"benchmark": 'ZZ500',
"money": 5000000,
"startDate": '2013-04-01',
"endDate": '2018-04-12',
"turnover": 10,
"tradingHabit": "OpenPrice",
"status":0
}
inputJson = \
{
"strategyID":'2',
"stockPicker":{"pick":False,"dateList":["2017-05-05"]},
"criteriaDict" : {"存货周转率":["最近一年",None,None],"主营收入同比":["最近一年",None,None],"流动资产":["最近一期",None,None]},
"portforlio": "SZ50",
"benchmark": "ZZ500",
"money": 5000000,
"startDate": "2017-04-06",
"endDate": "2018-04-12",
"turnover": 10,
"tradingHabit": "OpenPrice",
"status":0
}
starttime = time.time()
myBT = engine2.Engine(inputJson) #initialize engine
if myBT.stockPicker['pick'] == False:
myBT.backTesting()
evaluator(myBT.strategyID,myBT.BTRecords,myBT.IndexRecords,plot = False)
elif myBT.stockPicker['pick'] == True:
for day in myBT.stockPicker['dateList']:
myBT.stockPicking(day) #是选的哪一天的???
endtime = time.time()
print('it takes %0.2f seconds to finish BT'%(endtime-starttime))
# -*- coding: utf-8 -*-
"""
Created on 20180330
@author: zhoushuai
"""
'''
created by: matt 2018-03-30
EMMIDD2.dbo.CY_FinacialRatio_RP
SecuCode 证券代码 varchar(12) FALSE
SecuAbbr 证券名称 nvarchar(40) TRUE
EndDate 报告日期 datetime FALSE
PublDate 公告日期 datetime TRUE
InventoryTurnover 存货周转率 numeric(18,4) 次 TRUE
AccountsReceivableTurnover 应收账款周转率 numeric(18,4) 次 TRUE
FixedAssetsTurnover 固定资产周转率 numeric(18,4) 次 TRUE
OperatingRevenueYoY 主营收入同比 numeric(18,4) TRUE
DeductNetProfitYoY 归属于母公司股东的扣除非经常性损益后的净利润同比 numeric(18,4) TRUE
'''
import pandas as pd
import pyodbc
import ky
import datetime
sdk = ky.Api("http://data-api.kuaiyutech.com/api.rpc")
import logging
logger = logging.getLogger(__name__)
try:
from ..BT.financialRatioRP import FinancialRatioRP
logger.setLevel(logging.CRITICAL)
from ..util.kySDK import *
from .apiParent import ApiParent
except:
import sys
sys.path.insert(0, 'D:/Project/YM/BackTesting Frame/KYBT/BT')
sys.path.insert(0, 'D:/Project/YM/BackTesting Frame/KYBT/util')
print(sys.path)
from financialRatioRP import FinancialRatioRP
from kySDK import *
from apiParent import ApiParent
logger.setLevel(logging.WARNING)
class API(ApiParent):
def __init__(self,DSN):
super().__init__(DSN)
@staticmethod
def pdToValueObjectList(panda):
FinancialRatioRPList = []
for index,row in panda.iterrows():
oneFinancialRatioRP = FinancialRatioRP()
oneFinancialRatioRP.SecuCode = row['SecuCode']
oneFinancialRatioRP.SecuAbbr = row['SecuAbbr']
oneFinancialRatioRP.EndDate = row['EndDate']
oneFinancialRatioRP.PublDate = row['PublDate']
oneFinancialRatioRP.InventoryTurnover = row['InventoryTurnover']
oneFinancialRatioRP.AccountsReceivableTurnover = row['AccountsReceivableTurnover']
oneFinancialRatioRP.FixedAssetsTurnover = row['FixedAssetsTurnover']
oneFinancialRatioRP.OperatingRevenueYoY = row['OperatingRevenueYoY']
oneFinancialRatioRP.DeductNetProfitYoY = row['DeductNetProfitYoY']
FinancialRatioRPList.append(oneFinancialRatioRP)
return FinancialRatioRPList
def removeDuplicated(self,myPD):
# find two or more identical EndDate, delete one random row
# allStockPD.drop_duplicates(subset = date,keep ='last',inplace = True) # just that easy
seen = set()
uniqObj = []
for x,y in zip(self.pdToValueObjectList(myPD),[j.EndDate for j in self.pdToValueObjectList(myPD)]):
if y not in seen:
#print(seen)
uniqObj.append(x)
seen.add(y)
return uniqObj
def getRaw(self,endDate,stockList):
'''
获取从endDate开始往前推近四年的所有data,每年4个
如果有两个2013-06-30,去Null少的那个
CY_ValueDaily_Quote is daily data
stock: list, ## [600000,000002]
startDate: str, '2018-01-10'
endDate: str,'2018-03-03'
'''
#
itemString = '''
SecuCode,
SecuAbbr,
EndDate,
PublDate,
InventoryTurnover,
AccountsReceivableTurnover,
FixedAssetsTurnover,
OperatingRevenueYoY,
DeductNetProfitYoY
'''
#
if len(stockList) == 1:
stockList = '(%s)'%stockList[0]
else:
stockList = tuple(stockList)
#
startDate = self.getNdayBefore(endDate,365*4)
#
query = '''
SELECT
%s
FROM
[dbo].[CY_FinacialRatio_RP]
where
EndDate <= '%s 00:00:00:000'
AND
EndDate >= '%s 00:00:00:000'
AND
isValid = '1'
AND SecuCode IN %s
ORDER BY
SecuAbbr DESC,
EndDate DESC
'''%(itemString,endDate,startDate,stockList)
#
#print(query)
self.cursor.execute(query)
raw = self.cursor.fetchall()
#print('raw :',raw[0],type(raw[0]))
if not raw:
logger.critical( 'CY_FinacialRatio_RP value has no data, return None')
return None
financialRatioPD = self.pandaData(raw,itemString)
financialRatioPD.fillna(value=0, inplace=True) # to replace None with value 0
# take care of multiple stocks in one single big pandas sheet
# get rid of duplicated rows in pandas, replace with removed myPD
result = self.pdToDict(financialRatioPD)
for SecuCode,myPD in result.items():
result[SecuCode] = self.removeDuplicated(myPD)
# {'600000':[obj,obj],'000002':[obj,obj]} 每个SecoCode里,最新的obj在最前面
return result
def validateStockList(self,stockList,spotDate,criteria,period,upperbound,lowerbound,):
mydict = dict()
dictPD = self.getRaw(spotDate,stockList)
for SecuCode,objList in dictPD.items():
mydict[SecuCode] = self.validateOneStock(SecuCode,spotDate,objList,criteria,period,upperbound,lowerbound)
#print('mydict',mydict)
return mydict
def validateOneStock(self,oneStock,spotDate,objList,criteria,period,upperbound,lowerbound,):
'''
CY_ValueDaily_Quote is daily data
stockList: list, ## [600000,000002]
spotDate: str, '2015-03-01'|'now'
period: str, '最近一期|最近一年|最近三年|'
EndDate PublDate
2017-09-30 00:00:00.000 2017-10-28 00:00:00.000
2017-06-30 00:00:00.000 2017-08-30 00:00:00.000
2017-03-31 00:00:00.000 2017-04-27 00:00:00.000
2016-12-31 00:00:00.000 2017-04-01 00:00:00.000
2016-09-30 00:00:00.000 2017-10-28 00:00:00.000
2016-06-30 00:00:00.000 2017-08-30 00:00:00.000
2016-03-31 00:00:00.000 2017-04-27 00:00:00.000
2015-12-31 00:00:00.000 2017-04-01 00:00:00.000
2015-09-30 00:00:00.000 2016-10-29 00:00:00.000
2015-06-30 00:00:00.000 2016-08-11 00:00:00.000
2015-03-31 00:00:00.000 2016-04-30 00:00:00.000
2014-12-31 00:00:00.000 2016-04-07 00:00:00.000
2014-09-30 00:00:00.000 2015-10-30 00:00:00.000
2014-06-30 00:00:00.000 2015-08-20 00:00:00.000
'''
dateList = [str(x.EndDate) for x in objList]
spotYear = spotDate[:4]
#print(spotYear)
# 确定选股的基准时间点,现在为时间基点 or 以前某时间为基点
if spotDate == 'now':
spotDate = getYesterday() # 选昨日不会错,选今日可能错
#
if period == '最近一期': #找到最近的一个PublDate, 先用标准日期代替PublDate
objWanted = [objList[0]]
elif period == '最近一年':
if spotDate + '-12-31' in dateList[0]:
# 已发布
objWanted = [dateList[0]]
else:
# 未发布
objWanted = self.findObj('EndDate',[str(eval(spotYear)-1) + '-12-31', str(eval(spotYear)-2) + '-12-31'],objList)[:1] # 只要前一个
elif period == '最近三年':
if '12-31' in dateList[0]:
# 已发布
recentThreeYears = [spotYear,str(eval(spotYear)-1),str(eval(spotYear)-2)]
recentThreeDates = [x + '-12-31' for x in recentThreeYears]
objWanted = self.findObj('EndDate',recentThreeDates,objList)[:3] # 只要前三个
else:
# 未发布
recentThreeYears = [str(eval(spotYear)-1),str(eval(spotYear)-2),str(eval(spotYear)-3),str(eval(spotYear)-4)]
recentThreeDates = [x + '-12-31' for x in recentThreeYears]
objWanted = self.findObj('EndDate',recentThreeDates,objList)[:3] # 只要前三个
#print('objWanted',objWanted)
#for x in objWanted:
# print(x.DeductNetProfitYoY)
# print(x.printMe())
#print('objWanted: ',objWanted)
tell = self.isItTrue(objWanted,upperbound,lowerbound,criteria)
#print('tell',tell)
return tell
def isItTrue(self,objWanted,upperbound,lowerbound,criteria):
'''
criteria: list,['营业收入增长率','归母净利润增长率','应收账款周转率','存货周转率','固定资产周转率']
'''
if upperbound == None:
upperbound = 1000000
if lowerbound == None:
lowerbound = -1000000
# 全真则为真,否则为假
result = False if False in [True if (eval('x.%s'%criteria) > lowerbound and eval('x.%s'%criteria) < upperbound) else False for x in objWanted] else True
#print('True or False result: ',result)
return result
def test(self):
query = 'SELECT TOP 10 * FROM embase2.[dbo].[CY_FinacialIndicators_RP]'
self.cursor.execute(query)
#print(self.cursor.fetchall())
if __name__ == '__main__':
logger.warn(__file__)
import time
myAPI = API('BTEMmidd2')
myAPI.init()
#myAPI.test()
#y = myAPI.getRaw('2017-06-06',['000002','600000'])
stockList = ['600000','000002','600485','300357','600000','000002','600485','300357','600000','000002','600485','300357']
myAPI.validateStockList(stockList,'2015-11-29','DeductNetProfitYoY','最近三年',None,5,)
#print(y[0].printMe())
'''
#找出最近一期所有符合条件的股票
start = time.time()
for a in range(len(stockList)):
myAPI.getRaw('2017-06-06',['000002'])
end = time.time()
print('it takes %f to run one for %d times'%(end-start,len(stockList)))
start = time.time()
myAPI.getRaw('2017-06-06',stockList)
end = time.time()
print('it takes %f to run stockList for one time'%(end-start))
'''
\ No newline at end of file
# -*- coding: utf-8 -*-
"""
Created on 20180330
@author: zhoushuai
"""
'''
列名 中文名 别名 类型 单位 默认值 是否可空 说明
SecuCode 证券代码 varchar(12) FALSE
SecuAbbr 证券名称 nvarchar(40) TRUE
EndDate 报告日期 datetime FALSE
TotalAssets 总资产 numeric(18,4) 元 TRUE
AdvanceReceipts 预收款项 numeric(18,4) TRUE
EquityBelongedToPC 净资产 numeric(18,4) TRUE
PublDate 公告日期 datetime TRUE
TotalLiability 总负债 numeric(18,4) 元 TRUE
TotalCurrentLiability 流动负债 numeric(18,4) 元 TRUE
TotalCurrentAssets 流动资产 numeric(18,4) 元 TRUE
CashOrDepositInCentralBank 现金及存放中央银行款项 numeric(18,4) 元 TRUE
NetProfit 净利润 numeric(18,4) 元 TRUE
NetOperateCashFlow 经营活动产生的现金流 numeric(18,4) 元 TRUE
'''
import pandas as pd
import pyodbc
import ky
import datetime
sdk = ky.Api("http://data-api.kuaiyutech.com/api.rpc")
import logging
logger = logging.getLogger(__name__)
try:
from ..BT.financialIndicator import FinancialIndicator
logger.setLevel(logging.CRITICAL)
from ..util.kySDK import *
from .apiParent import ApiParent
except:
import sys
sys.path.insert(0, 'D:/Project/YM/BackTesting Frame/KYBT/BT')
sys.path.insert(0, 'D:/Project/YM/BackTesting Frame/KYBT/util')
print(sys.path)
from financialIndicator import FinancialIndicator
from kySDK import *
from apiParent import ApiParent
logger.setLevel(logging.WARNING)
class API(ApiParent):
def __init__(self,DSN):
super().__init__(DSN)
@staticmethod
def pdToValueObjectList(panda):
financialIndicatorList = []
for index,row in panda.iterrows():
oneFinancialIndicator = FinancialIndicator()
oneFinancialIndicator.SecuCode = row['SecuCode']
oneFinancialIndicator.SecuAbbr = row['SecuAbbr']
oneFinancialIndicator.EndDate = row['EndDate']
oneFinancialIndicator.AdvanceReceipts = float(row['AdvanceReceipts'])
oneFinancialIndicator.TotalAssets = float(row['TotalAssets'])
oneFinancialIndicator.EquityBelongedToPC = float(row['EquityBelongedToPC'])
oneFinancialIndicator.TotalLiability = float(row['TotalLiability'])
oneFinancialIndicator.TotalCurrentAssets = float(row['TotalCurrentAssets'])
oneFinancialIndicator.TotalCurrentLiability = float(row['TotalCurrentLiability'])
oneFinancialIndicator.CashOrDepositInCentralBank = float(row['CashOrDepositInCentralBank'])
oneFinancialIndicator.NetProfit = float(row['NetProfit'])
oneFinancialIndicator.NetOperateCashFlow = float(row['NetOperateCashFlow'])
financialIndicatorList.append(oneFinancialIndicator)
return financialIndicatorList
def getFinancialIndicator(self,startDate,endDate,stockList):
'''
CY_ValueDaily_Quote is daily data
stock: list, ## [600000,000002]
startDate: str, '2018-01-10'
endDate: str,'2018-03-03'
'''
#
itemString = '''
SecuCode,
SecuAbbr,
EndDate,
AdvanceReceipts,
TotalAssets,
EquityBelongedToPC,
TotalLiability,
TotalCurrentAssets,
TotalCurrentLiability,
CashOrDepositInCentralBank,
NetProfit,
NetOperateCashFlow
'''
#
if len(stockList) == 1:
stockList = '(%s)'%stockList[0]
else:
stockList = tuple(stockList)
#
query = '''
SELECT
%s
FROM
[dbo].[CY_FinacialIndicators_RP]
where
EndDate <= '%s 00:00:00:000'
AND
EndDate >= '%s 00:00:00:000'
AND SecuCode IN %s
ORDER BY
SecuAbbr DESC,
EndDate Asc
'''%(itemString,endDate,startDate,stockList)
#
#print(query)
self.cursor.execute(query)
raw = self.cursor.fetchall()
logger.info('raw :',raw)
if not raw:
logger.critical( 'CY_FinacialIndicators_RP value has no data, return None')
return None
financialIndicatorPD = self.pandaData(raw,itemString)
financialIndicatorPD.fillna(value=0, inplace=True) # to replace None with value 0
return self.pdToValueObjectList(financialIndicatorPD)
if __name__ == '__main__':
logger.warn(__file__)
myAPI = API('BTEMmidd2')
myAPI.init()
y = myAPI.getFinancialIndicator('2017-01-05','2017-06-06',['600000','000002'])
print(y)
\ No newline at end of file
# -*- coding: utf-8 -*-
"""
Created on 20180309
@author: zhoushuai
"""
'''
估值指标 (EMMIDD2.dbo.CY_ValueDaily_Quote)
PE(TTM) 最新一期/最近一年/最近三年 小于10 10~15 15~20 20~40 40~60 60~80 80~100 大于100 小于0
PB 最新一期/最近一年/最近三年 小于1 1~1.5 1.5~2 2~2.5 2.5~3.5 3.5~5 大于5
PS 最新一期/最近一年/最近三年 大于10 10~5 5~3 3~1 1~0 小于0
PE预测 最新一期/最近一年/最近三年 小于10 10~15 15~20 20~40 40~60 60~80 80~100 大于100 小于0
股息率 最新一期/最近一年/最近三年 大于5% 5%~3% 3%~1% 小于1%
市值指标 (EMMIDD2.dbo.CY_ValueDaily_Quote)
总市值 最新一期/最近一年/最近三年 巨盘 大盘 中盘 小盘 微盘
流通市值 最新一期/最近一年/最近三年 巨盘 大盘 中盘 小盘 微盘
有效流通市值 最新一期/最近一年/最近三年 巨盘 大盘 中盘 小盘 微盘
大于1000亿 500~1000亿 200~500亿 50~200亿 小于50亿
最新一期:这个季度和上个季度相比
最近一年:今年和去年相比
最近三年:今年和前年相比
watch this out
Tradedate ReportedPeriod
600000 浦发银行 2017-12-27 00:00:00.000 2017-09-30 00:00:00.000 1.45000000
600000 浦发银行 2017-12-28 00:00:00.000 2017-09-30 00:00:00.000 1.45000000
600000 浦发银行 2017-12-29 00:00:00.000 2017-09-30 00:00:00.000 1.45000000
600000 浦发银行 2018-01-02 00:00:00.000 2017-12-31 00:00:00.000 1.84000000
600000 浦发银行 2018-01-03 00:00:00.000 2017-12-31 00:00:00.000 1.84000000
600000 浦发银行 2018-01-04 00:00:00.000 2017-12-31 00:00:00.000 1.84000000
2018-01-02 is the most recently updated date, get the data of this day,
方大炭素10-30号公布财报,这一天是财报的
TradeDate ReportedPeriod PE1
600516 方大炭素 2017-10-26 00:00:00.000 2017-06-30 00:00:00.000 .23024118 106.1764 59.8503 7.5299
600516 方大炭素 2017-10-27 00:00:00.000 2017-06-30 00:00:00.000 1.16400000 103.4796 11.8980 7.3386
600516 方大炭素 2017-10-30 00:00:00.000 2017-09-30 00:00:00.000 1.16400000 24.9367 18.5979 6.0679
600516 方大炭素 2017-10-31 00:00:00.000 2017-09-30 00:00:00.000 1.16400000 25.9524 19.3553 6.3150
600516 方大炭素 2017-11-01 00:00:00.000 2017-09-30 00:00:00.000 1.16400000 25.8366 19.2690 6.2868
原来9-30号是最新的一期,那我就只要用ReportedPeried的第一个03-31,06-30,09-30,12-31
PE(TTM)最近三年小于10
简单的来处理,就取三个时间点就好了,因为净利润一年只可能知道4次,那么准确的PE其实也只有4个点的值而已。
因为不可能知道企业每一天的净利,所以平时滚动的PE其实也是大家的不准确估计吧
'''
import pandas as pd
import pyodbc
import ky
import datetime
sdk = ky.Api("http://data-api.kuaiyutech.com/api.rpc")
import logging
logger = logging.getLogger(__name__)
try:
from ..BT.value import Value
from ..BT.index import Index
logger.setLevel(logging.CRITICAL)
from ..util.kySDK import *
from .apiParent import ApiParent
except:
import sys
sys.path.insert(0, 'D:/Project/YM/BackTesting Frame/KYBT/BT')
sys.path.insert(0, 'D:/Project/YM/BackTesting Frame/KYBT/util')
print(sys.path)
from value import Value
from index import Index
from kySDK import *
from apiParent import ApiParent
logger.setLevel(logging.WARNING)
class API(ApiParent):
def __init__(self,DSN):
super().__init__(DSN)
@staticmethod
def pdToValueObjectList(panda):
valueList = []
for index,row in panda.iterrows():
oneValue = Value()
oneValue.SecuCode = row['SecuCode']
oneValue.SecuAbbr = row['SecuAbbr']
oneValue.TradeDate = row['TradeDate']
oneValue.ReportPeriod = row['ReportPeriod']
oneValue.PE1 = float(row['PE1'])
oneValue.PB = float(row['PB'])
oneValue.PS = float(row['PS'])
oneValue.ClosePrice = float(row['ClosePrice'])
oneValue.RMBShares = float(row['RMBShares'])
oneValue.ValidShares = float(row['ValidShares'])
oneValue.MarketValue = float(row['MarketValue'])
valueList.append(oneValue)
return valueList
def getSpotValue(self,startDate,endDate,stockList):
'''
CY_ValueDaily_Quote is daily data
stock: list, ## [600000,000002]
startDate: str, '2018-01-10'
endDate: str,'2018-03-03'
'''
#
itemString = '''
SecuCode,
SecuAbbr,
TradeDate,
ReportPeriod,
EPS,
PE1,
PE2,
PB,
PS,
Dividend1Y,
MarketValue,
ClosePrice,
ValidShares,
RMBShares,
Bshares'''
#
if len(stockList) == 1:
stockList = '(%s)'%stockList[0]
else:
stockList = tuple(stockList)
#
query = '''
SELECT
%s
FROM
[dbo].[CY_ValueDaily_Quote]
where
TradeDate <= '%s 00:00:00:000'
AND
TradeDate >= '%s 00:00:00:000'
AND SecuCode IN %s
ORDER BY
SecuAbbr DESC,
TradeDate Asc
'''%(itemString,endDate,startDate,stockList)
#
#print(query)
self.cursor.execute(query)
raw = self.cursor.fetchall()
logger.info('raw :',raw)
if not raw:
logger.critical( 'CY_ValueDaily_Quote value has no data, return None')
return None
valuePD = self.pandaData(raw,itemString)
valuePD.fillna(value=0, inplace=True) # to replace None with value 0
return self.pdToValueObjectList(valuePD)
if __name__ == '__main__':
logger.warn(__file__)
myAPI = API('BTEMmidd2')
myAPI.init()
y = myAPI.getSpotValue('2017-05-05','2017-06-06',['600000'])
print(y)
\ No newline at end of file
import pyodbc
import logging
import pandas as pd
logger = logging.getLogger(__name__)
logger.setLevel(logging.CRITICAL)
import functools
import warnings
import datetime
import sys
import logging
logger = logging.getLogger(__name__)
try:
from ..emClasses import *
logger.setLevel(logging.CRITICAL)
from ..util.kySDK import *
from ..dataTable import DataTable
except:
import sys
#D:\Project\益盟\回测框架\KYBT\BT
sys.path.insert(0, 'D:/Project/YM/BackTesting Frame/KYBT/BT')
sys.path.insert(0, 'D:/Project/YM/BackTesting Frame/KYBT/util')
#print(sys.path)
from emClasses import *
from dataTable import DataTable
from kySDK import *
logger.setLevel(logging.WARNING)
class ApiParent():
'''
parent of all apis
myAPI = APIPARENT('BTMarketTrade')
myAPI.init()
or
myAPI = APIPARENT('BTMarketTrade')
with myAPI():
do something
'''
# '2009以后不考虑,数据好像有问题'
FinancialReportDate = {'Mar':'03-31','Jun':'06-30','Sep':'09-30','Dec':'12-31'}
def __init__(self):
self.DSN = 'BTMarketTrade'
def init(self):
try:
self.cnxn = pyodbc.connect(DSN = self.DSN,UID="SLReadOnly",PWD="gBNfZ9zPCzVD")
self.cursor = self.cnxn.cursor()
except:
raise 'connection failed'
def __enter__(self): # with
self.init()
def __exit__(self,*args): # with
self.cnxn.close()
self.cnxn = None
@staticmethod
def getNeededTable(inputJson):
tableList = ["EM_QUOTE_STOCK_DAILY"] #
for key in inputJson['criteriaDict']:
if key in DataTable:
tableList.append(DataTable[key][1]) # 把需要用到的数据表加进去
else:
raise ValueError('%s is not in the available table'%key)
#print('需要用到的数据表: ', tableList)
return tableList
def pdToObjList(self,panda,objName,itemID):
'''
dictObj: dict('date':[objList],'date':[objList],...)
for example:
{'2017-01-26': [<stock.Stock object at 0x0857FA70>, <stock.Stock object at 0x0857FB30>],
'2017-01-25': [<stock.Stock object at 0x0857FB10>, <stock.Stock object at 0x0857FAB0>],
'''
if objName in ['Stock','ValueDaily','Index']:
date = 'TradeDate'
else:
date = 'EndDate'
itemList = self.modifyItemString(itemID)
dateList = [str(x)[:10] for x in set(panda[date])]
dateList.sort(reverse = True)
dictObj = dict()
for oneDate in dateList:
pandaSection = panda[panda[date] == oneDate]
objList = []
for index,row in pandaSection.iterrows():
exec("oneObj = %s()"%objName)
for item in itemList:
if item in ['TradeDate','EndDate']:
exec("oneObj.%s = str(row['%s'])[:10]"%(item,item))
elif item in ['SecuCode','SecuAbbr','SecuMarket','PublDate','TradeStatus']:
exec("oneObj.%s = str(row['%s'])"%(item,item))
elif item in ['LLimitUpTime']:
exec("oneObj.%s = str(row['%s'])[:10]"%(item,item))
else:
#print(item,row[item])
exec("oneObj.%s = float(row['%s'])"%(item,item))
exec("objList.append(oneObj)")
dictObj[oneDate] = objList
#print(dictObj[oneDate][-1].TradeDate)
#print('dictObj: ',dictObj)
return dictObj
def findTableColumns(self,tableName,itemString):
query = "SELECT %s FROM sys.columns WHERE object_id = OBJECT_ID('dbo.%s')"%(itemString,tableName)
self.cursor.execute(query)
logger.warn('findTableColumns:query: ',query)
columnData = self.cursor.fetchall()
if not columnData:
logger.warn( '%s has no data, return None'%tableName )
return None
self.columnName = [x[1] for x in columnData]
return self.columnName
@staticmethod
def findObj(attr,value,objList):
#print('findObj',attr,value,objList)
objListTemp = list()
for x in objList:
for y in value:
#print(y, ' aaaaaaaaaa ',str(eval('x.%s'%attr)))
if y in str(eval('x.%s'%attr)) :
#print(y, ' aaaaaaaaaa ',str(eval('x.%s'%attr)))
objListTemp.append(x)
return objListTemp
@staticmethod
def modifyItemString(itemString):
'''
itemString = '\n SecuCode,SecuAbbr,SecuMarket,TradeDate,TradeStatus,\n PreClosePrice'
'''
listContainer = []
#print(itemString)
for x in itemString.split(','):
if x.startswith('\n'):
listContainer.append(x[2:].strip())
else:
listContainer.append(x)
return listContainer
def pandaData(self,rawData,itemString):
PDcontainer = []
for x in rawData:
tempData = [y for y in x]
PDcontainer.append(tempData)
#print('tempData',tempData,type(tempData))
#print('PDcontainer:',PDcontainer)
myPD = pd.DataFrame(PDcontainer)
#print('myPD,itemString:',myPD,itemString)
myPD.columns = self.modifyItemString(itemString)
logger.info(myPD[:10])
return myPD
#########################################################################################
########################### take care of date ############################################
def getTradingDays(self,startDate,endDate):
'''
获得交易日,如果HS300,SZ50,ZZ500当天任何一家有交易,就认为当天是交易日
'''
itemString = 'SecuCode,TradeDate'
index = ('000300','000905','000016')
query = '''
SELECT
%s
FROM
embase2.[dbo].[EM_QUOTE_INDEX_DAILY]
WHERE
SecuCode in %s
AND
TradeDate >= '%s 00:00:00.000'
AND TradeDate <= '%s 00:00:00.000'
ORDER BY TradeDate ASC
'''%(itemString,index,startDate,endDate)
#print(query)
self.cursor.execute(query)
raw = self.cursor.fetchall()
logger.info('raw :',raw)
if not raw:
logger.critical( 'index market value|trading days| has no data, return None')
return None
indexPD = self.pandaData(raw,itemString)
indexPD.fillna(value=0, inplace=True) # to replace None with value 0
dateList = [str(x)[:10] for x in indexPD['TradeDate'].tolist()]
dateList = list(set(dateList))
dateList.sort()
today = datetime.datetime.strftime(datetime.datetime.today(),'%Y-%m-%d')
for x in list(dateList):
if x >= today:
#print('future date',x)
dateList.remove(x)
return dateList
def getToday(self):
return str(datetime.datetime.today())[:10]
def getYesterday(self):
end = datetime.datetime.today()
start = end - datetime.timedelta(1)
return str(start)[:10]
def getNdayBefore(self,date,N):
end = datetime.datetime.strptime(date,'%Y-%m-%d')
start = end - datetime.timedelta(N)
return str(start)[:10]
def getMostRecentTrading(self):
end = datetime.datetime.today()
start = end - datetime.timedelta(70)
end = datetime.datetime.strftime(end,'%Y-%m-%d')
start = datetime.datetime.strftime(start,'%Y-%m-%d')
return self.getTradingDays(end,start)[-1]
def getMostRecentTradingDay(self,then):
'''
比如说想找 then = '2015-06-05' 这天之前的最近一个交易日
'''
end = datetime.datetime.strptime(then,'%Y-%m-%d')
start = end - datetime.timedelta(70)
end = datetime.datetime.strftime(end,'%Y-%m-%d')
start = datetime.datetime.strftime(start,'%Y-%m-%d')
return self.getTradingDays(end,start)[-1]
@staticmethod
def deprecated(func):
"""This is a decorator which can be used to mark functions
as deprecated. It will result in a warning being emitted
when the function is used."""
@functools.wraps(func)
def new_func(*args, **kwargs):
warnings.simplefilter('always', DeprecationWarning) # turn off filter
warnings.warn("Call to deprecated function {}.".format(func.__name__),
category=DeprecationWarning,
stacklevel=2)
warnings.simplefilter('default', DeprecationWarning) # reset filter
return func(*args, **kwargs)
return new_func
if __name__ == '__main__':
myAPI = ApiParent()
myAPI.init()
myAPI.getTradingDays()
\ No newline at end of file
# -*- coding: utf-8 -*-
"""
Created on 20180309
@author: zhoushuai
"""
import pandas as pd
import pyodbc
import ky
sdk = ky.Api("http://data-api.kuaiyutech.com/api.rpc")
import logging
logger = logging.getLogger(__name__)
try:
from ..BT.stock import Stock
from ..BT.index import Index
logger.setLevel(logging.CRITICAL)
from ..util.kySDK import *
from .apiParent import ApiParent
except:
import sys
#D:\Project\益盟\回测框架\KYBT\BT
sys.path.insert(0, 'D:/Project/YM/BackTesting Frame/KYBT/BT')
sys.path.insert(0, 'D:/Project/YM/BackTesting Frame/KYBT/util')
print(sys.path)
from stock import Stock
from index import Index
from kySDK import *
from apiParent import ApiParent
logger.setLevel(logging.WARNING)
class API(ApiParent):
def __init__(self,DSN):
super().__init__(DSN)
def getGrowthRateOperatingRevenue(self,date,stock,option,range):
'''
主营业务收入增长率
date: str '2017-01-01'
stock: list ## [600000,000002]
range float, ## 大于30%, 30%~20%, 20%~15%, 15%~10%, 10%~7.5%, 7.5%~5%, 5%~2.5%, 2.5%~0, 小于0,
option 'recent period'|'recent year'|'recent three year' ## 最近一期,最近一年,最近三年
'''
itemString = 'SecuCode,SecuAbbr,TradeDate,TradeStatus,PreClosePrice,OpenPrice,ClosePrice,HighPrice,LowPrice,TurnoverVolume,TurnoverValue,ChangeRatio'
query = "SELECT %s FROM [dbo].[CY_FinacialRatio_RP] where SecuCode = %s ORDER BY EndDate DESC "
#print(query)
self.cursor.execute(query)
raw = self.cursor.fetchall()
logger.info('raw :',raw)
if not raw:
logger.critical( 'index market value has no data, return None')
return None
indexPD = self.pandaData(raw,itemString)
indexPD.fillna(value=0, inplace=True) # to replace None with value 0
return self.pdToIndexObjectList(indexPD)
if __name__ == '__main__':
logger.warn(__file__)
myAPI = API('BTEMmidd2')
myAPI.init()
y = myAPI.getGrowthRateOperatingRevenue('2017-05-05','600000','recent period',0.3,)
print(y)
\ No newline at end of file
# read a Json, then download all data needed after
import pandas as pd
import pyodbc
import ky
import datetime
import sys
import time
sdk = ky.Api("http://data-api.kuaiyutech.com/api.rpc")
import os
import logging
logger = logging.getLogger(__name__)
from .apiParent import ApiParent
#print(__file__)
#print(os.path.dirname(os.path.abspath(__file__)))
#print(os.path.dirname(os.path.abspath('initRobot.py')))
try:
from ..BT.dataTable import DataTable
logger.setLevel(logging.CRITICAL)
from ..util.kySDK import *
from .apiParent import ApiParent
except:
import sys
#D:\Project\益盟\回测框架\KYBT\BT
sys.path.insert(0, 'D:/Project/YM/BackTesting Frame/KYBT/BT')
sys.path.insert(0, 'D:/Project/YM/BackTesting Frame/KYBT/util')
#print(sys.path)
from emClasses import *
from dataTable import DataTable
from kySDK import *
logger.setLevel(logging.WARNING)
class highwayAPI(ApiParent):
'''
get data from SQL and organize it
'''
def __init__(self):
super().__init__()
def getAllStockCode(self,oneDay):
'''
return ['600000','000002',...]
'''
itemString = 'SecuCode'
query = '''
SELECT DISTINCT %s
FROM
[dbo].[EM_QUOTE_STOCK_DAILY]
WHERE
TradeDate = '%s 00:00:00.000'
AND
left(SecuCode,1) != 9
AND
left(SecuCode,1) != 2
'''%(itemString,oneDay)
self.cursor.execute(query)
raw = self.cursor.fetchall()
if not raw:
logger.critical( 'Daily Quote all has no data, return None')
return None
allStockPD = self.pandaData(raw,'SecuCode')
allStockPD.fillna(value=0, inplace=True) # to replace None with value 0
#print('hahahahaha',allStockPD[itemString].tolist())
return allStockPD[itemString].tolist()
def getIndexData(self,index,startDate,endDate):
if isinstance(index,str):
if index == 'HS300':
index = "('000300')"
elif index == 'ZZ500':
index = "('000905')"
elif index == 'SZ50':
index = "('000016')"
else:
raise ValueError('Index input is incorrect')
else:
raise ValueError('Index input should be str')
if endDate >= self.getYesterday():
endDate = self.getYesterday()
itemID = '''SecuCode,SecuAbbr,TradeDate,TradeStatus,PreClosePrice,OpenPrice,ClosePrice,HighPrice,LowPrice,
TurnoverVolume,TurnoverValue,ChangeRatio'''
#
query = '''
SELECT
%s
FROM
embase2.[dbo].[EM_QUOTE_INDEX_DAILY]
WHERE
SecuCode in %s
AND TradeDate >= '%s 00:00:00.000'
AND TradeDate <= '%s 00:00:00.000'
ORDER BY
TradeDate DESC
'''%(itemID,index,startDate,endDate)
#print(query)
self.cursor.execute(query)
raw = self.cursor.fetchall()
if not raw:
return None
allStockPD = self.pandaData(raw,itemID)
allStockPD.fillna(value=0, inplace=True) # to replace None with value 0
return self.pdToObjList(allStockPD,'Index',itemID)
@staticmethod
def backwardStartDate(inputJson,startDate,tableName):
# 此表如果需要最近三年,则需要在下载数据的时候把endDate提前3年,保险起见,提前5年
# 最近一年, 提前3年
# 最近一期,提前2年
# {'主营收入同比':['最近三年',None,-100]}
for criteria in inputJson['criteriaDict']:
# 如果'最近三年' 在 EM_QUOTE_STOCK_DAILY 这个数据表里
#print(inputJson[ID]['criteriaDict'][criteria])
#print(DataTable[criteria][1])
if inputJson['criteriaDict'][criteria][0] == '最近三年' and DataTable[criteria][1] == tableName:
startDate = str(eval('int(startDate[:4]) - 5')) + startDate[4:]
break
elif inputJson['criteriaDict'][criteria][0] == '最近一年' and DataTable[criteria][1] == tableName:
startDate = str(eval('int(startDate[:4]) - 3')) + startDate[4:]
break
elif inputJson['criteriaDict'][criteria][0] == '最近一期' and DataTable[criteria][1] == tableName:
startDate = str(eval('int(startDate[:4]) - 2')) + startDate[4:]
break
return startDate
def getTableData(self,inputJson,tableName):
'''
get data from embase2.tableName
'''
stockList = inputJson['portforlio']
startDate = inputJson['startDate']
endDate = inputJson['endDate']
if endDate < startDate:
raise ValueError('endDate cannot be smaller than startDate')
# index pool
if isinstance(stockList,str) and stockList in ['HS300','ZZ500','SZ50']:
if stockList == 'HS300':
stockList = 'SHSE.000300'
elif stockList == 'ZZ500':
stockList = 'SHSE.000905'
elif stockList == 'SZ50':
stockList = 'SHSE.000016'
stockList = [stockCode[-6:] for stockCode in getIndexPoolFromKy(stockList)] #['SHSE.600000','SHSE.000002']
elif isinstance(stockList,str) and portforlio == 'all':
stockList = self.getAllStockCode(startDate)
elif isinstance(stockList,list):
pass
else:
raise ValueError('stockList input is incorrect')
# date
if tableName in ['EM_QUOTE_STOCK_DAILY','CY_ValueDaily_Quote']:
date = 'TradeDate'
else:
date = 'EndDate'
# itemID objName
if tableName == 'EM_QUOTE_STOCK_DAILY':
itemID = '''SecuCode,SecuAbbr,SecuMarket,TradeDate,TradeStatus,PreClosePrice,OpenPrice,ClosePrice,HighPrice,
LowPrice,TurnoverVolume,TurnoverValue,TurnoverDeals,Amplitude,ChangeRatio,
FlowShares,FlowMarketValue,TotalEquity,IssueEquity,TotalValue,LLimitUpTime,RiseOrDown'''
objName = 'Stock'
elif tableName == 'CY_FinacialRatio_RP':
itemID = '''SecuCode,SecuAbbr,EndDate,PublDate,InventoryTurnover,AccountsReceivableTurnover,FixedAssetsTurnover,
OperatingRevenueYoY,DeductNetProfitYoY'''
objName = 'FinancialRatioRP'
startDate = self.backwardStartDate(inputJson,startDate,tableName)
elif tableName == 'CY_FinacialIndicators_RP':
itemID = '''SecuCode,SecuAbbr,EndDate,AdvanceReceipts,TotalAssets,EquityBelongedToPC,TotalLiability,TotalCurrentAssets,
TotalCurrentLiability,CashOrDepositInCentralBank,NetProfit,NetOperateCashFlow'''
objName = 'FinancialIndicator'
startDate = self.backwardStartDate(inputJson,startDate,tableName)
elif tableName == 'CY_ValueDaily_Quote':
itemID = '''SecuCode,SecuAbbr,TradeDate,ReportPeriod,EPS,PE1,PE2,PB,PS,Dividend1Y,MarketValue,ClosePrice,ValidShares,
RMBShares,Bshares'''
objName ='ValueDaily'
startDate = self.backwardStartDate(inputJson,startDate,tableName)
else:
raise ValueError('%s is not a defined Class'%tableName)
#
if len(stockList) == 1:
stockList = '(%s)'%stockList[0]
else:
stockList = tuple(stockList)
#
query = '''
SELECT
%s
FROM
embase2.[dbo].[%s]
WHERE
SecuCode in %s
AND %s >= '%s 00:00:00.000'
AND %s <= '%s 00:00:00.000'
ORDER BY
SecuCode DESC,
%s DESC
'''%(itemID,tableName,stockList,date,startDate,date,endDate,date)
#print(query)
self.cursor.execute(query)
raw = self.cursor.fetchall()
#print(raw)
if not raw:
return None
allStockPD = self.pandaData(raw,itemID)
allStockPD.fillna(value=0, inplace=True) # to replace None with value 0
return self.pdToObjList(allStockPD,objName,itemID)
def getAllNeeded(self,inputJson):
"""
根据inputJson获取所有需要的数据,bulk格式为:
{'EM_QUOTE_STOCK_DAILY':{date:[stockObj]},
'CY_FinacialRatio_RP':{date:[financialRatioObj]},
...
'EM_QUOTE_INDEX_DAILY':{date}:[indexObj]}
"""
endDate = inputJson['endDate']
startDate = inputJson['startDate']
benchmark = inputJson['benchmark']
bulk = dict()
print('开始下载并整理数据...')
a = time.time()
for tableName in super().getNeededTable(inputJson):
#print('tableName: ',tableName)
bulk[tableName] = self.getTableData(inputJson,tableName)
print('已经完成%s所需的下载'%tableName)
# 指数要单独,因为input不一样
bulk['EM_QUOTE_INDEX_DAILY'] = self.getIndexData(benchmark,startDate,endDate)
print('下载整理数据完毕,共耗时%0.1f秒'%(time.time() - a))
print('包含的数据表有:',list(bulk.keys()))
print(sys.getsizeof(bulk))
return bulk
if __name__ == '__main__':
def extractBulk(bulk,portforlio):
'''
extract from self.getAllNeeded(inputJson)
return is
{'600485': {'主营收入同比': [Decimal('13.2225'), Decimal('33.8532'), Decimal('851.6680')]},
'600000': {'主营收入同比': [Decimal('18.9713'), Decimal('18.9713'), Decimal('23.1625'), Decimal('23.1625'), Decimal('20.5
697'), Decimal('20.5697')]},
'000002': {'主营收入同比': [Decimal('33.5828'), Decimal('8.1002'), Decimal('31.3263')]}}
'''
resultDict = dict()
for stock in portforlio:
for criteria in inputJson['criteriaDict']:
if inputJson['criteriaDict'][criteria][0] == '最近三年':
for spotDate in list(bulk['CY_FinacialRatio_RP'].keys()): #dict_keys(['2015-12-31', '2015-09-30', '2015-06-30', '2015-03-31', '2014-12-31'])
if '-12-31' in spotDate:
firstYear = spotDate #'2015-12-31'
secondYear = str(eval('int(firstYear[:4]) - 1')) + firstYear[4:] #'2014-12-31'
thirdYear = str(eval('int(firstYear[:4]) - 2')) + firstYear[4:] #'2013-12-31'
break
#找到相对应的criteria数据
criteriaList = []
for year in [firstYear,secondYear,thirdYear]:
for finObj in bulk['CY_FinacialRatio_RP'][year]:
#print(finObj.SecuCode,finObj.EndDate)
if finObj.SecuCode == stock and finObj.EndDate == year:
criteriaList.append(eval('finObj.%s'%(DataTable[criteria][0])))
resultDict[stock] = {criteria:criteriaList}
elif inputJson['criteriaDict'][criteria][0] == '最近一年':
for spotDate in list(bulk['CY_FinacialRatio_RP'].keys()): #dict_keys(['2015-12-31', '2015-09-30', '2015-06-30', '2015-03-31', '2014-12-31'])
if '-12-31' in spotDate:
firstYear = spotDate #'2015-12-31'
break
#找到相对应的criteria数据
criteriaList = []
for finObj in bulk['CY_FinacialRatio_RP'][firstYear]:
#print(finObj.SecuCode,finObj.EndDate)
if finObj.SecuCode == stock and finObj.EndDate == firstYear:
criteriaList.append(eval('finObj.%s'%(DataTable[criteria][0])))
resultDict[stock] = {criteria:criteriaList}
elif inputJson[ID]['criteriaDict'][criteria][0] == '最近一期':
firstPeriod = list(bulk['CY_FinacialRatio_RP'].keys())[0] #'2015-12-31'
#找到相对应的criteria数据
criteriaList = []
for finObj in bulk['CY_FinacialRatio_RP'][firstPeriod]:
#print(finObj.SecuCode,finObj.EndDate)
if finObj.SecuCode == stock and finObj.EndDate == firstPeriod:
criteriaList.append(eval('finObj.%s'%(DataTable[criteria][0])))
resultDict[stock] = {criteria:criteriaList}
else:
raise ValueError('输入要是最近一期,最近一年,最近三年')
print('resultDict: ',resultDict,)
return resultDict
def screenStock(inputJson,resultDict):
"""
{'600485': {'主营收入同比': [Decimal('13.2225'), Decimal('33.8532'), Decimal('851.6680')]},
'600000': {'主营收入同比': [Decimal('18.9713'), Decimal('18.9713'), Decimal('23.1625'), Decimal('23.1625'), Decimal('20.5
697'), Decimal('20.5697')]},
'000002': {'主营收入同比': [Decimal('33.5828'), Decimal('8.1002'), Decimal('31.3263')]}}
{'600485':True,
'600000':False,
...
}
"""
screenResult = dict()
for stock,value in resultDict.items():
multiple = []
for criteria,valueList in value.items():
upperbound = inputJson[ID]['criteriaDict'][criteria][1]
lowerbound = inputJson[ID]['criteriaDict'][criteria][2]
if upperbound == None:
upperbound = 1000000
if lowerbound == None:
lowerbound = -1000000
# 全真则为真,否则为假
single = False if False in [True if x > lowerbound and x < upperbound else False for x in valueList] else True #[Decimal('13.2225'), Decimal('33.8532'), Decimal('851.6680')]
multiple.append(single)
consilidatedResult = False if False in multiple else True
screenResult[stock] = consilidatedResult
print('True or False result: ',screenResult)
return screenResult
def getTrueStock(screenResult):
mylist = []
for key,value in screenResult.items():
if value == True:
mylist.append(key)
print('getTrueStock: ',mylist)
return mylist
ID = '1'
# "criteriaDict" : {'存货周转率':['最近一年',None,-100],"PE(TTM)":["PE","CY_ValueDaily_Quote"]},
# ['600485','600000','000002'],
strategy = \
{
"stockPicker":{'pick':False,'dateList':None},
"criteriaDict" : {'主营收入同比':['最近三年',None,18]},
"portforlio": ['600485','600000','000002'],
"benchmark": 'HS300',
"money": 5000000,
"startDate": '2014-07-01',
"endDate": '2016-02-01',
"turnover": 10,
"tradingHabit": "OpenPrice",
"status":"init"
}
inputJson = \
{
ID:strategy
}
##### results
results = \
{
"dailyResults":{'2016-01-07':'result1','2016-01-08':'result2',},
"status":"completed"
}
outputJson = \
{
ID:'completed'
}
#tableName = 'EM_QUOTE_STOCK_DAILY'
#tableName = 'CY_FinacialRatio_RP'
#tableName = 'CY_FinacialIndicators_RP'
#tableName = 'CY_ValueDaily_Quote'
#startDate = '2017-01-01'
#endDate = '2018-02-01'
#stockID = ['600000','000002']
##############
myAPI = highwayAPI()
myAPI.init()
portforlio = inputJson[ID]['portforlio']
startDate = inputJson[ID]['startDate']
endDate = inputJson[ID]['endDate']
#tableList = ['EM_QUOTE_INDEX_DAILY']
bulk = myAPI.getAllNeeded(inputJson)
#选出当日符合条件的股票,比如 inputJson[ID]['criteriaDict'] = {'主营收入同比':['最近三年',None,-100]}
#print(bulk['CY_FinacialRatio_RP'].keys())
resultDict = myAPI.extractBulk(bulk,portforlio)
screenDict = myAPI.screenStock(inputJson,resultDict)
screenStocks = myAPI.getTrueStock(screenDict)
\ No newline at end of file
# -*- coding: utf-8 -*-
"""
Created on 20180309
@author: zhoushuai
"""
import pandas as pd
import pyodbc
import ky
import datetime
sdk = ky.Api("http://data-api.kuaiyutech.com/api.rpc")
import logging
logger = logging.getLogger(__name__)
try:
from ..BT.stock import Stock
from ..BT.index import Index
logger.setLevel(logging.CRITICAL)
from ..util.kySDK import *
from .apiParent import ApiParent
except:
import sys
#D:\Project\益盟\回测框架\KYBT\BT
sys.path.insert(0, 'D:/Project/YM/BackTesting Frame/KYBT/BT')
sys.path.insert(0, 'D:/Project/YM/BackTesting Frame/KYBT/util')
print(sys.path)
from stock import Stock
from index import Index
from kySDK import *
from apiParent import ApiParent
logger.setLevel(logging.WARNING)
class API(ApiParent):
'''
get data from SQL and organize it
'''
def __init__(self):
super().__init__()
@staticmethod
def pdToStockObjectList(panda):
stockList = []
for index,row in panda.iterrows():
oneStock = Stock()
oneStock.SecuCode = row['SecuCode']
oneStock.SecuAbbr = row['SecuAbbr']
oneStock.SecuMarket = row['SecuMarket']
oneStock.TradeDate = row['TradeDate']
oneStock.TradeStatus = row['TradeStatus']
oneStock.PreClosePrice = float(row['PreClosePrice'])
oneStock.OpenPrice = float(row['OpenPrice'])
oneStock.ClosePrice = float(row['ClosePrice'])
oneStock.HighPrice = float(row['HighPrice'])
oneStock.LowPrice = float(row['LowPrice'])
oneStock.TurnoverVolume = float(row['TurnoverVolume'])
oneStock.TurnoverValue = float(row['TurnoverValue'])
oneStock.TurnoverDeals = float(row['TurnoverDeals'])
oneStock.Amplitude = float(row['Amplitude'])
oneStock.ChangeRatio = float(row['ChangeRatio'])
oneStock.FlowShares = float(row['FlowShares'])
oneStock.FlowMarketValue = float(row['FlowMarketValue'])
oneStock.TotalEquity = float(row['TotalEquity'])
oneStock.IssueEquity = float(row['IssueEquity'])
oneStock.TotalValue = float(row['TotalValue'])
oneStock.RecordID = row['RecordID']
oneStock.LLimitUpTime = str(row['LLimitUpTime'])
oneStock.RiseOrDown = float(row['RiseOrDown'])
stockList.append(oneStock)
return stockList
@staticmethod
def pdToIndexObjectList(panda):
indexList = []
for index,row in panda.iterrows():
indexOneDay = Index()
indexOneDay.SecuCode = row['SecuCode']
indexOneDay.SecuAbbr = row['SecuAbbr']
indexOneDay.TradeDate = row['TradeDate']
indexOneDay.PreClosePrice = float(row['PreClosePrice'])
indexOneDay.OpenPrice = float(row['OpenPrice'])
indexOneDay.ClosePrice = float(row['ClosePrice'])
indexOneDay.HighPrice = float(row['HighPrice'])
indexOneDay.LowPrice = float(row['LowPrice'])
indexOneDay.TurnoverVolume = float(row['TurnoverVolume'])
indexOneDay.TurnoverValue = float(row['TurnoverValue'])
indexOneDay.ChangeRatio = float(row['ChangeRatio'])
indexList.append(indexOneDay)
return indexList
def getMarketValueOneStock(self,startDate,endDate,stockID):
'''
get market value of one stock
'''
itemString = '''SecuCode,
SecuAbbr,SecuMarket,TradeDate,TradeStatus,PreClosePrice,
OpenPrice,ClosePrice,HighPrice,LowPrice,TurnoverVolume,TurnoverValue,TurnoverDeals,Amplitude,ChangeRatio,
FlowShares,FlowMarketValue,TotalEquity,IssueEquity,TotalValue,RecordID,LLimitUpTime,RiseOrDown'''
query = '''
SELECT
%s
FROM
[dbo].[EM_QUOTE_STOCK_DAILY]
WHERE
SecuCode = '%s'
AND TradeDate >= '%s 00:00:00.000'
AND TradeDate <= '%s 00:00:00.000'
'''%(itemString,stockID,startDate,endDate)
self.cursor.execute(query)
raw = self.cursor.fetchall()
logger.info('raw :',raw)
if not raw:
logger.critical( 'Daily Quote has no data, return None')
return None
ReportsPD = self.pandaData(raw,itemString)
ReportsPD.fillna(value=0, inplace=True) # to replace None with value 0
return self.pdToStockObjectList(ReportsPD)
def getAllStockCode(self,oneDay):
'''
return ['600000','000002',...]
'''
itemString = 'SecuCode'
query = '''
SELECT DISTINCT %s
FROM
[dbo].[EM_QUOTE_STOCK_DAILY]
WHERE
TradeDate = '%s 00:00:00.000'
AND
left(SecuCode,1) != 9
AND
left(SecuCode,1) != 2
'''%(itemString,oneDay)
self.cursor.execute(query)
raw = self.cursor.fetchall()
logger.info('raw :',raw)
if not raw:
logger.critical( 'Daily Quote all has no data, return None')
return None
allStockPD = self.pandaData(raw,'SecuCode')
allStockPD.fillna(value=0, inplace=True) # to replace None with value 0
#print('hahahahaha',allStockPD[itemString].tolist())
return allStockPD[itemString].tolist()
def getIndexMarketValue(self,startDate,endDate,index):
'''
get market value of one specific index
'''
if index == 'HS300':
index = '000300'
elif index == 'ZZ500':
index = '000905'
elif index == 'SZ50':
index = '000016'
else:
raise ValueError('Index is incorrect')
itemString = 'SecuCode,SecuAbbr,TradeDate,TradeStatus,PreClosePrice,OpenPrice,ClosePrice,HighPrice,LowPrice,TurnoverVolume,TurnoverValue,ChangeRatio'
query = '''
SELECT
%s
FROM
[dbo].[EM_QUOTE_INDEX_DAILY]
WHERE
SecuCode = '%s'
AND
TradeDate >= '%s 00:00:00.000'
AND TradeDate <= '%s 00:00:00.000'
ORDER BY TradeDate DESC
'''%(itemString,index,startDate,endDate)
#print(query)
self.cursor.execute(query)
raw = self.cursor.fetchall()
logger.info('raw :',raw)
if not raw:
logger.critical( 'index market value has no data, return None')
return None
indexPD = self.pandaData(raw,itemString)
indexPD.fillna(value=0, inplace=True) # to replace None with value 0
return self.pdToIndexObjectList(indexPD)
def getMarketValueOneDay(self,oneDay,index):
'''
get stocks' marketValue of 'all'|'HS300'|'SZ500'|'SZ50'|['600000','000002',...]
index can be: 'all'|'HS300'|'SZ500'|'SZ50'|['600000','000002',...]
'''
itemString = '''SecuCode,
SecuAbbr,SecuMarket,TradeDate,TradeStatus,PreClosePrice,
OpenPrice,ClosePrice,HighPrice,LowPrice,TurnoverVolume,TurnoverValue,TurnoverDeals,Amplitude,ChangeRatio,
FlowShares,FlowMarketValue,TotalEquity,IssueEquity,TotalValue,RecordID,LLimitUpTime,RiseOrDown'''
if (isinstance(index,str) and index == 'all') or None:
#print('@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@')
query = '''
SELECT %s
FROM
[dbo].[EM_QUOTE_STOCK_DAILY]
WHERE
TradeDate = '%s 00:00:00.000'
'''%(itemString,oneDay)
self.cursor.execute(query)
raw = self.cursor.fetchall()
logger.info('raw :',raw)
if not raw:
logger.critical( 'Daily Quote all has no data, return None, %s'%oneDay)
return None
allStockPD = self.pandaData(raw,itemString)
allStockPD.fillna(value=0, inplace=True) # to replace None with value 0
return self.pdToStockObjectList(allStockPD)
elif index in ['HS300','ZZ500','SZ50']:
#print('@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@')
# specify index
if index == 'HS300':
index = 'SHSE.000300'
elif index == 'ZZ500':
index = 'SHSE.000905'
elif index == 'SZ50':
index = 'SHSE.000016'
#getStock
try:
stockPool = tuple(stockCode[-6:] for stockCode in getIndexPoolFromKy(index)) #['SHSE.600000','SHSE.000002'] tuple has no sequences
except:
raise
query = '''
SELECT %s
FROM
[dbo].[EM_QUOTE_STOCK_DAILY]
WHERE
TradeDate = '%s 00:00:00.000'
AND
SecuCode IN %s
'''%(itemString,oneDay,stockPool)
#print('query: %s',query)
self.cursor.execute(query)
raw = self.cursor.fetchall()
logger.info('raw :',raw)
if not raw:
logger.critical( 'Daily Quote of specified index has no data, return None, %s'%oneDay)
return None
indexStockPD = self.pandaData(raw,itemString)
indexStockPD.fillna(value=0, inplace=True) # to replace None with value 0
return self.pdToStockObjectList(indexStockPD)
elif isinstance(index,list):
#getStock
#print('@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@',index)
try:
stockPool = tuple(stockCode[-6:] for stockCode in index) # no sequence
except:
raise
if len(index) > 1:
stockPool = tuple(stockCode[-6:] for stockCode in index) # no sequence
elif len(index) == 1:
stockPool = '(%s)'%index[0]
query = '''
SELECT %s
FROM
[dbo].[EM_QUOTE_STOCK_DAILY]
WHERE
TradeDate = '%s 00:00:00.000'
AND
SecuCode IN %s
'''%(itemString,oneDay,stockPool)
#print(query)
self.cursor.execute(query)
raw = self.cursor.fetchall()
logger.info('raw :',raw)
if not raw:
logger.critical( 'Daily Quote of specified stocks has no data, return None, %s'%oneDay)
return None
indexStockPD = self.pandaData(raw,itemString)
indexStockPD.fillna(value=0, inplace=True) # to replace None with value 0
return self.pdToStockObjectList(indexStockPD)
if __name__ == '__main__':
logger.warn(__file__)
myAPI = API('BTMarketTrade')
myAPI.init()
stockList = ['600000','000002']
'''
for stock in stockList:
logger.critical(stock)
#logger.warn(myAPI.getMarketValueOneStock('2015-05-05','2016-05-05',stock))
logger.warn(len(myAPI.getMarketValueOneDay('2015-05-05','SZ50')))
myStock = myAPI.getMarketValueOneDay('2015-05-05','SZ50')
for x in myStock:
x.printMe()
myStock = myAPI.getMarketValueOneDay('2015-05-05',stockList)
for x in myStock:
x.printMe()
'''
#x = myAPI.getAllStockCode()
#print(x)
start = '2017-05-01'
end = '2017-06-15'
x = myAPI.getTradingDays(start,end)
print(x)
import sys
sys.path.insert(0, 'D:/Project/益盟/回测框架/KYBT/util')
from kySDK import *
"""
print(getMostRecentTradingDayFromKy())
itemString = '''SecuCode,
SecuAbbr,SecuMarket,TradeDate,TradeStatus,PreClosePrice,
OpenPrice,ClosePrice,HighPrice,LowPrice,TurnoverVolume,TurnoverValue,TurnoverDeals,Amplitude,ChangeRatio,
FlowShares,FlowMarketValue,TotalEquity,IssueEquity,TotalValue,RecordID,RiseOrDown'''
index = 'SHSE.000016'
index = 'SHSE.000001'
stockPool = tuple(stockCode[-6:] for stockCode in getIndexPoolFromKy(index))
query = '''
SELECT %s
FROM
[dbo].[EM_QUOTE_STOCK_DAILY]
WHERE
TradeDate = '%s 00:00:00.000'
AND
SecuCode IN %s
'''%(itemString,getMostRecentTradingDayFromKy(),stockPool)
print(query)
"""
def myfun(x):
print(sum(x))
myfun([1,2,3])
class myclass():
a = 1
def myfunc(self):
print(self.a)
c = myclass()
c.myfunc()
# -*- coding: utf-8 -*-
"""
Created on 20180311
@author: Matthew
"""
from .position import Position
import logging
import numpy as np
logger = logging.getLogger(__name__)
logger.setLevel(logging.CRITICAL)
class Account():
def __str__(self):
s = 'class account: you can buy, hold, and dump all stocks in this class'
return s
def __init__(self,cash,tradingHabit):
'''
tradingHabit: 'OpenPrice'|'ClosePrice'| OpenPrice is prefered
'''
self.initialCash = cash
self.__cash = cash
self.positionList = []
self.__totalValue = cash #
self.__positionListValue = 0 #
self.tradingHabit = tradingHabit
self.runningDays = 0
def printMe(self):
logger.warn(' --------- my account status -----------')
logger.warn(' account--initialCash: %0.2f'%self.initialCash)
logger.warn(' account--cash: %0.2f'%self.cash)
logger.warn(' account--positionListValue: %0.2f'%self.positionListValue)
logger.warn(' account--totalValue: %0.2f'%self.totalValue)
logger.warn(' account--len of positionList: %d'%len(self.positionList))
#print('account--Name of positionList',[x.SecuCode for x in self.positionList])
logger.warn(' ---------------------------------------')
@property
def cash(self):
return self.__cash
@cash.setter
def cash(self,value):
if value < -1:
raise ValueError('cash below -1 is not possible')
self.__cash = value
@property
def totalValue(self):
return self.__totalValue
@totalValue.setter
def totalValue(self,value):
if value < -1:
raise ValueError('totalValue below -1 is not possible')
self.__totalValue = value
@property
def positionListValue(self):
return self.__positionListValue
@positionListValue.setter
def positionListValue(self,value):
if value < -1:
raise ValueError('positionListValue below -1 is not possible, can be below 0 slightly because of the rounding')
self.__positionListValue = value
def isempty(self):
if len(self.positionList) > 0:
return False
else:
return True
def buy(self,stock,evenCash):
'''
excecute the buy action, evenCash is total cash / stock #
'''
#print('buy')
#print('buy','stock.TradeStatus',stock.TradeStatus,stock.SecuCode)
price = stock.OpenPrice if self.tradingHabit == 'OpenPrice' else stock.ClosePrice
boughtAmount = np.floor(evenCash/price/100)*100
#print('evenCash: %d, price: %0.2f, boughtAmount: %s, boughtValue: %d'%(evenCash,price,boughtAmount,price*boughtAmount))
#print('boughtAmount:%d'%boughtAmount)
if boughtAmount <= 0:
#print('not enough evenCash,evenCash: %d,price %0.2f, boughtAmount:%d'%(evenCash,price,boughtAmount))
return None
#create Position Object
stockPosition = Position(stock,boughtAmount,price)
#print('SecuCode,price,amount,value,',stockPosition.SecuCode,stockPosition.boughtPrice,stockPosition.boughtAmount,stockPosition.positionValue)
#update positionList
self.positionList.append(stockPosition)
#print('self.positionList: ',self.positionList)
self.cash -= price * boughtAmount
#print('self.cash:',self.cash)
#updata positionValue
self.positionListValue += price * boughtAmount
#update totalValue
self.totalValue = self.positionListValue + self.cash
#print('totalValue: %d'%self.totalValue)
#self.runningDays += 1
def countrunningDays(self):
self.runningDays += 1
def getPositionList(self):
return self.positionList
def getnetValue(self):
return self.getTotalValue()/self.initialCash
def getTotalValue(self):
return self.totalValue
def updatePositionListValue(self):
#print('updatePositionListValue')
#print(x.positionValue for x in self.positionList)
#value = 0
for x in self.positionList:
#print(x.SecuCode,x.positionValue)
if x.positionValue == None: # 君正集团 20130704 没有bar
x.positionValue = 0
#value += x.positionValue
#return value
return sum(x.positionValue for x in self.positionList)
def getPositionListvalue(self):
return self.positionListValue
def sellAvailableForSell(self,NameList):
'''
dump all available position
'''
self.initialCash = self.cash
#print('sellAvailableForSell') # print here means execute once in that loop
for stockName in NameList:
for position in self.positionList:
if position.SecuCode == stockName:
#print('stockName: %s, position.positionValue: %s, positionListValue: %s, len(positionList): %d'\
#%(stockName,position.positionValue,self.positionListValue,len(self.positionList)))
self.initialCash += position.positionValue
self.cash += position.positionValue
self.positionListValue -= position.positionValue
self.positionList.remove(position)
#self.runningDays += 1
def sell(self,stock):
'''
sell individual stock
'''
for position in self.positionList:
if position.SecuCode == stock.SecuCode:
self.positionList.remove(position) # don't need to empty position object, python will clean the cache after certain while
def held(self,day,updatedStock):
'''
update position's market value in the positionList
'''
#logger.warning('held')
myList = [x.SecuCode for x in updatedStock]
if not myList:
return 'no positions to hold 1 '
#print('myList: ',myList,[x.ClosePrice for x in updatedStock])
if updatedStock is None:
print('no position to hold 2')
return 'No data for %s'%day
for position in self.positionList:
for stock in updatedStock:
if position.SecuCode == stock.SecuCode:
#print('match',position.SecuCode,position.SecuCode)
position.remainInAccount(stock,2) # method1 or method2 in position.py
self.positionListValue = self.updatePositionListValue()
self.totalValue = self.positionListValue + self.cash
# -*- coding: utf-8 -*-
"""
Created on 2018408
@author: Matthew
"""
# 首先找到所有需要用到的表
# 所有的指标中文、英文名、数据表名称
DataTable = \
{
"存货周转率":["InventoryTurnover","CY_FinacialRatio_RP"],
"应收账款周转率":["AccountsReceivableTurnover","CY_FinacialRatio_RP"],
"固定资产周转率":["FixedAssetsTurnover","CY_FinacialRatio_RP"],
"主营收入同比":["OperatingRevenueYoY","CY_FinacialRatio_RP"],
"归属于母公司股东的扣除非经常性损益后的净利润同比":['DeductNetProfitYoY',"CY_FinacialRatio_RP"],
"总资产":["TotalAssets","CY_FinacialIndicators_RP"],
"预收款项":["AdvanceReceipts","CY_FinacialIndicators_RP"],
"净资产":["EquityBelongedToPC","CY_FinacialIndicators_RP"],
"总负债":["TotalLiability","CY_FinacialIndicators_RP"],
"流动负债":["TotalCurrentLiability","CY_FinacialIndicators_RP"],
"流动资产":["TotalCurrentAssets","CY_FinacialIndicators_RP"],
"现金及存放中央银行款项":["CashOrDepositInCentralBank","CY_FinacialIndicators_RP"],
"净利润":["NetProfit","CY_FinacialIndicators_RP"],
"经营活动产生的现金流":["NetOperateCashFlow","CY_FinacialIndicators_RP"],
"最新流通A股":["RMBShares","CY_ValueDaily_Quote"],
"最新有效流通股":["ValidShares","CY_ValueDaily_Quote"],
"最新总市值":["MarketValue","CY_ValueDaily_Quote"],
"市盈率(TTM)":["PE1","CY_ValueDaily_Quote"],
"市销率":["PB","CY_ValueDaily_Quote"],
"市净率":["PS","CY_ValueDaily_Quote"],
}
def findTable(criteria):
for key in DataTable:
if key == criteria:
DataTable[key]
'''
myBT = engine(portforlio,money,startDate,endDate,turnover,benchmark,price)
myBT.backTesting()
myBT.getDailyResult()
'''
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.CRITICAL) # higher level, less showing
from ..util.kySDK import *
from ..API import marketValueAPI
from ..API import CY_FinacialRatio_RP
from .position import Position
from .account import Account
from datetime import datetime
import os
import time
import sys
class Engine():
myAPI = marketValueAPI.API('BTMarketTrade')
myAPI.init()
myAPI2 = CY_FinacialRatio_RP.API('BTEMmidd2')
myAPI2.init()
def __init__(self,strategyID,portforlio,initialCash,startDate,endDate,benchmark,tradingHabit,criteriaDict):
if endDate < startDate:
raise ValueError('endDate cannot be smaller than startDate')
if isinstance(portforlio,str) and portforlio in ['HS300','ZZ500','SZ50']:
if portforlio == 'HS300':
portforlio = 'SHSE.000300'
elif portforlio == 'ZZ500':
portforlio = 'SHSE.000905'
elif portforlio == 'SZ50':
portforlio = 'SHSE.000016'
self.portforlio = [stockCode[-6:] for stockCode in getIndexPoolFromKy(portforlio)] #['SHSE.600000','SHSE.000002']
elif isinstance(portforlio,str) and portforlio == 'all':
self.portforlio = self.myAPI.getAllStockCode(startDate)
elif isinstance(portforlio,list):
self.portforlio = portforlio
print('len(self.portforlio)',len(self.portforlio))
self.strategyID = strategyID
self.initialCash = initialCash
self.startDate = startDate
self.endDate = endDate
self.benchmark = benchmark
self.tradingHabit = tradingHabit
self.BTRecords = dict()
self.IndexRecords = None
self.tradingDayHistory = list() # will help get the previous trading day
self.criteriaDict = criteriaDict
self.turnover =
def screenTrueStocks(self,stockList,criteriaDict,spotDate):
#
TrueStocks = set()
for key,values in criteriaDict.items():
#print(stockList,spotDate,key,values[0],values[1],values[2])
temp = self.myAPI2.validateStockList(stockList,spotDate,key,values[0],values[1],values[2])
for k,v in temp.items():
if v == True:
TrueStocks.add(k)
return list(TrueStocks)
@staticmethod
def printRecord(mydict):
#print('mydict: ',mydict)
for x in mydict:
print(x,mydict[x])
def keepRecord(self,oneDay,positionList,totalValue,cash,positionListValue):
'''
put daily BT record into Json
'''
self.BTRecords[oneDay] = dict()
self.BTRecords[oneDay]['positionList'] = positionList
self.BTRecords[oneDay]['totalValue'] = totalValue
self.BTRecords[oneDay]['cash'] = cash
self.BTRecords[oneDay]['positionListValue'] = positionListValue
self.tradingDayHistory.append(oneDay)
if not os.path.exists('./Strategy Records'):
os.makedirs('./Strategy Records')
with open('./Strategy Records/strategy_%s.txt'%self.strategyID,'a+') as f:
f.write('--------' + oneDay + '----------\n')
f.write('positionList: %s\n'%len(positionList))
f.write('cash: %s\n'%cash)
f.write('positionListValue: %s\n'%positionListValue)
f.write('totalValue: %s\n'%totalValue)
f.write('\n')
def getRecord(self,oneDay):
return self.BTRecords[oneDay]
def backTesting(self,turnover):
logger.critical('################################ backtesting ###################################: %s'%(self.strategyID))
myaccount = Account(self.initialCash,'OpenPrice')
'''
tradingHabit: 'OpenPrice'|'ClosePrice'| OpenPrice is prefered
criteriaDict: {'DeductNetProfitYoY':[None,5,'最近一期']} #dict(item:[period,upperbound,lowerbound])
if turnover is int, then turnover for every N days
if turnover is None, then buy and sell according to specific condition,
if turnover is flexible, then buy and sell according to specific condition, + turnover days
'''
time.sleep(2)
#print('aaaaaaaaaaaaaaaaaaaaaa','./Strategy Records/strategy_%s.txt'%(self.strategyID))
try:
with open('./Strategy Records/strategy_%s.txt'%self.strategyID,'w+') as f:
f.write(' |' + self.strategyID + '| \n')
except :
e = sys.exc_info()[0]
print( "<p>Error: %s</p>" % e )
if turnover == 0:
turnover == 100000000
if isinstance(turnover,int) and turnover > 0: # keep turnover
totalRuningDays = 1
countDays = 1
for day in self.myAPI.getTradingDays(self.startDate,self.endDate):
logger.critical('%s ########## %s ############ totalRuningDays %s'%(self.strategyID,day,totalRuningDays))
myaccount.printMe()
self.keepRecord(day,myaccount.positionList,myaccount.totalValue,myaccount.cash,myaccount.positionListValue) # keep daily BT record
if len(self.tradingDayHistory) > 1:
previousTradingDay = self.tradingDayHistory[-2]
previousRecord = self.getRecord(previousTradingDay)
previousPositionList = previousRecord['positionList']
if countDays == 1:
# first day, buy
# screen for qualified stocks
trueStocks = self.screenTrueStocks(self.portforlio,self.criteriaDict,day)
#print('buy ',trueStocks,' ' ,len(trueStocks))
# update
if len(trueStocks) == 0:
print('%s has no data, continue'%day)
countDays += 1
totalRuningDays += 1
continue
marketValueOneDay = self.myAPI.getMarketValueOneDay(day,trueStocks)
logger.warn(marketValueOneDay)
if marketValueOneDay is None: #if marketValueOneDay has no data
print('%s has no data, continue'%day)
countDays += 1
totalRuningDays += 1
continue
for stock in list(marketValueOneDay):
#print('stock.LLimitUpTime',stock.LLimitUpTime,' ',type(stock.LLimitUpTime)) # exclude Tingpai or LimitUp before the market is open
if stock.TradeStatus == '0':
print('TradeStatus = "0" , 当日交易被禁止,从需买列表中删除之,:%s'%stock.SecuCode)
marketValueOneDay.remove(stock)
if stock.RiseOrDown == '1' and datetime.strptime(stock.LLimitUpTime[11:],'%H:%M:%S') < datetime(1900, 1, 1, 9, 30,0):
print('9:30以前涨停,无法买入,从需买列表中删除之')
marketValueOneDay.remove(stock)
if len(self.tradingDayHistory) > 1:
if stock.SecuCode in [x.SecuCode for x in previousPositionList]:
print('昨日的positionList中已有该票,无法买入,从需买列表中删除之: %s'%stock.SecuCode)
if stock.SecuCode in [x.SecuCode for x in marketValueOneDay]:
marketValueOneDay.remove(stock)
#print('len(marketValueOneDay) ',len(marketValueOneDay))
for stock in marketValueOneDay: # buy the stock that is available to buy
#print('backtesting check: %s',stock.SecuCode)
#print('len(marketValueOneDay) ',len(marketValueOneDay))
#print('myaccount.cash ',myaccount.cash)
myaccount.buy(stock,myaccount.initialCash*0.95/len(marketValueOneDay)) # need to be very careful, make sure the initialCash won't change constantly
countDays += 1
totalRuningDays += 1
elif countDays < turnover:
temp = myaccount.held(day)
countDays += 1
totalRuningDays += 1
if isinstance(temp,str):
continue
elif countDays == turnover:
if myaccount.isempty():
#print('empty nothing to see in this account')
countDays = 1
totalRuningDays += 1
continue
positionStockName = [x.SecuCode for x in myaccount.positionList] # get positions in account and sell
positionStockOneDay = self.myAPI.getMarketValueOneDay(day,positionStockName) # list of stock
logger.warn(positionStockOneDay)
if marketValueOneDay is None: #if marketValueOneDay has no data
print('%s has no data, will sell the next day'%day)
totalRuningDays += 1
continue
for stock in list(positionStockOneDay):
#print(stock.TradeStatus)
#print('stock.LLimitUpTime',stock.LLimitUpTime,' ',type(stock.LLimitUpTime)) # exclude Tingpai or LimitUp before the market is open
if stock.TradeStatus == '0':
print('TradeStatus = "0" , 当日交易被禁止,从卖出列表中删除之,: %s'%stock.SecuCode)
positionStockOneDay.remove(stock)
if stock.RiseOrDown == '-1' and datetime.strptime(stock.LLimitUpTime[11:],'%H:%M:%S') < datetime(1900, 1, 1, 9, 30,0):
print('9:30以前跌停,无法卖出,从卖出列表中删除之') # 还没有考虑之后打开跌停的情况
positionStockOneDay.remove(stock)
myaccount.sellAvailableForSell([x.SecuCode for x in positionStockOneDay])
countDays = 1
totalRuningDays += 1
elif turnover is None:
if stock.SecuCode == '600000' and round(stock.OpenPrice)%2 == 1:
pass
elif turnover == 'flexible':
pass
self.getIndexResult()
logger.critical('################################ backtesting completed ###################################: %s'%(self.strategyID))
def getIndexResult(self):
indexList = self.myAPI.getIndexMarketValue(self.startDate,self.endDate,self.benchmark)
#print('indexList',indexList)
#print(indexList[-1].TradeDate,' ', indexList[-1].ClosePrice,' ', indexList[0].TradeDate,' ', indexList[0].ClosePrice)
#zhangfu1 = (indexList[-1].ClosePrice - indexList[0].ClosePrice ) / indexList[0].ClosePrice
#print('zhangfu of index', zhangfu1)
self.IndexRecords = indexList
#with open('result.txt','a+') as f:
# f.write(str(zhangfu1) + str(datetime.now()) + '\n')
#self.printRecord(self.BTRecords)
\ No newline at end of file
'''
created by: matt 2018-03-30
EMMIDD2.dbo.CY_FinacialIndicators_RP
列名 中文名 别名 类型 单位 默认值 是否可空 说明
SecuCode 证券代码 varchar(12) FALSE
SecuAbbr 证券名称 nvarchar(40) TRUE
EndDate 报告日期 datetime FALSE
TotalAssets 总资产 numeric(18,4) 元 TRUE
AdvanceReceipts 预收款项 numeric(18,4) TRUE
EquityBelongedToPC 净资产 numeric(18,4) TRUE
PublDate 公告日期 datetime TRUE
TotalLiability 总负债 numeric(18,4) 元 TRUE
TotalCurrentLiability 流动负债 numeric(18,4) 元 TRUE
TotalCurrentAssets 流动资产 numeric(18,4) 元 TRUE
CashOrDepositInCentralBank 现金及存放中央银行款项 numeric(18,4) 元 TRUE
NetProfit 净利润 numeric(18,4) 元 TRUE
NetOperateCashFlow 经营活动产生的现金流 numeric(18,4) 元 TRUE
'''
class FinancialIndicator():
def __init__(self):
pass
'''
def __init__(self,SecuCode,SecuAbbr,TradeDate,ReportPeriod,PE1,PB,PS,ClosePrice,RMBShares,Bshares,ValidShares,MarketValue):
self.SecuCode = SecuCode
self.SecuAbbr = SecuAbbr
self.EndDate = EndDate
self.PublDate = PublDate
self.TotalCurrentAssets = TotalCurrentAssets
self.TotalAssets = TotalAssets
self.AdvanceReceipts = AdvanceReceipts
self.EquityBelongedToPC = EquityBelongedToPC
self.CashOrDepositInCentralBank = CashOrDepositInCentralBank
self.TotalLiability = TotalLiability
self.TotalCurrentLiability = TotalCurrentLiability
self.NetProfit = NetProfit
self.NetOperateCashFlow = NetOperateCashFlow
'''
def printMe(self):
print(self.SecuCode,self.SecuAbbr,self.EndDate)
\ No newline at end of file
'''
created by: matt 2018-03-30
EMMIDD2.dbo.CY_FinacialRatio_RP
SecuCode 证券代码 varchar(12) FALSE
SecuAbbr 证券名称 nvarchar(40) TRUE
EndDate 报告日期 datetime FALSE
PublDate 公告日期 datetime TRUE
InventoryTurnover 存货周转率 numeric(18,4) 次 TRUE
AccountsReceivableTurnover 应收账款周转率 numeric(18,4) 次 TRUE
FixedAssetsTurnover 固定资产周转率 numeric(18,4) 次 TRUE
OperatingRevenueYoY 主营收入同比 numeric(18,4) TRUE
DeductNetProfitYoY 归属于母公司股东的扣除非经常性损益后的净利润同比 numeric(18,4) TRUE
'''
class FinancialRatioRP():
def __init__(self):
pass
'''
def __init__(self,SecuCode,SecuAbbr,TradeDate,ReportPeriod,PE1,PB,PS,ClosePrice,RMBShares,Bshares,ValidShares,MarketValue):
self.SecuCode = SecuCode
self.SecuAbbr = SecuAbbr
self.EndDate = EndDate
self.PublDate = PublDate
self.InventoryTurnover = InventoryTurnover
self.AccountsReceivableTurnover = AccountsReceivableTurnover
self.FixedAssetsTurnover = FixedAssetsTurnover
self.OperatingRevenueYoY = OperatingRevenueYoY
self.DeductNetProfitYoY = DeductNetProfitYoY
'''
def printMe(self):
print(self.SecuCode,self.SecuAbbr,self.EndDate,self.DeductNetProfitYoY)
\ No newline at end of file
'''
created by: matt 2018-03-15
'''
'''
列名 中文名 别名 类型 单位 默认值 是否可空 说明
SecuCode 证券代码 varchar(10) FALSE
SecuAbbr 证券简称 nvarchar(40) TRUE
TradeDate 交易日期 datetime FALSE
TradeStatus 交易状态 char(1) TRUE
PreClosePrice 昨收 numeric(20,4) 元 TRUE
OpenPrice 今开盘 numeric(20,4) 元 TRUE
ClosePrice 今收盘 numeric(20,4) 元 TRUE
HighPrice 最高价 numeric(20,4) 元 TRUE
LowPrice 最低价 numeric(20,4) 元 TRUE
TurnoverVolume 成交量 numeric(20,4) 手 TRUE
TurnoverValue 成交额 numeric(20,4) 元 TRUE
TurnoverDeals 成交笔数 numeric(20,4) 笔 TRUE
ChangeRatio 日涨跌幅 numeric(20,4) % TRUE
'''
class Index():
def __init__(self):
pass
'''
def __init__(self,SecuCode,SecuAbbr,SecuMarket,TradeDate,TradeStatus,PreClosePrice,OpenPrice,ClosePrice,HighPrice,
LowPrice,TurnoverVolume,TurnoverValue,TurnoverDeals,Amplitude,ChangeRatio,FlowShares,FlowMarketValue,TotalEquity,IssueEquity,
TotalValue,RecordID,RiseOrDown):
self.SecuCode = SecuCode
self.SecuAbbr = SecuAbbr
self.TradeDate = TradeDate
self.TradeStatus = TradeStatus
self.PreClosePrice = PreClosePrice
self.OpenPrice = OpenPrice
self.ClosePrice = ClosePrice
self.HighPrice = HighPrice
self.LowPrice = LowPrice
self.TurnoverVolume = TurnoverVolume
self.TurnoverValue = TurnoverValue
self.TurnoverDeals = TurnoverDeals
self.ChangeRatio = ChangeRatio
'''
def printMe(self):
print(self.SecuCode,self.SecuAbbr,self.TradeDate)
\ No newline at end of file
'''
created by: matt 2018-03-11
'''
'''
列名 中文名 别名 类型 单位 默认值 是否可空 说明
SecuCode 证券代码 varchar(10) FALSE
SecuAbbr 证券简称 nvarchar(40) TRUE
SecuMarket 证券市场 varchar(12) TRUE
TradeDate 交易日期 datetime FALSE
TradeStatus 交易状态 char(1) TRUE
PreClosePrice 昨收 numeric(20,4) 元 TRUE
OpenPrice 今开盘 numeric(20,4) 元 TRUE
ClosePrice 今收盘 numeric(20,4) 元 TRUE
HighPrice 最高价 numeric(20,4) 元 TRUE
LowPrice 最低价 numeric(20,4) 元 TRUE
TurnoverVolume 成交量 numeric(20,4) 手 TRUE
TurnoverValue 成交额 numeric(20,4) 元 TRUE
TurnoverDeals 成交笔数 numeric(20,4) 笔 TRUE
Amplitude 振幅 numeric(20,4) % TRUE
ChangeRatio 日涨跌幅 numeric(20,4) % TRUE
FlowShares 流通股本 numeric(30,4) TRUE
FlowMarketValue 流通市值 numeric(30,4) TRUE
TotalEquity 公司总股本 numeric(20,4) TRUE
IssueEquity 发行总股本 numeric(30,4) TRUE
TotalValue 总市值 numeric(30,4) TRUE
RecordID 记录ID uniqueidentifier (newid()) TRUE
RiseOrDown 涨跌标示 varchar(2) TRUE 1:涨停 -1:跌停 0:无涨跌停
'''
class Stock():
def __init__(self):
pass
'''
def __init__(self,SecuCode,SecuAbbr,SecuMarket,TradeDate,TradeStatus,PreClosePrice,OpenPrice,ClosePrice,HighPrice,
LowPrice,TurnoverVolume,TurnoverValue,TurnoverDeals,Amplitude,ChangeRatio,FlowShares,FlowMarketValue,TotalEquity,IssueEquity,
TotalValue,RecordID,LLimitUpTime,RiseOrDown):
self.SecuCode = SecuCode
self.SecuAbbr = SecuAbbr
self.SecuMarket = SecuMarket
self.TradeDate = TradeDate
self.TradeStatus = TradeStatus
self.PreClosePrice = PreClosePrice
self.OpenPrice = OpenPrice
self.ClosePrice = ClosePrice
self.HighPrice = HighPrice
self.LowPrice = LowPrice
self.TurnoverVolume = TurnoverVolume
self.TurnoverVal = TurnoverValue
self.TurnoverDeals = TurnoverDeals
self.Amplitude = Amplitude
self.ChangeRatio = ChangeRatio
self.FlowShares = FlowShares
self.FlowMarketValue = FlowMarketValue
self.TotalEquity = TotalEquity
self.IssueEquity = IssueEquity
self.TotalValue = TotalValue
self.RecordID = RecordID
self.LLimitUpTime = LLimitUpTime
self.RiseOrDown = RiseOrDown
'''
def printMe(self):
print('this is stock!')
#print(self.SecuCode,self.SecuAbbr,self.TradeDate)
\ No newline at end of file
'''
created by: matt 2018-03-11
EMMIDD2.dbo.CY_ValueDaily_Quote
列名 中文名 别名 类型 单位 默认值 是否可空 说明
SecuCode 证券代码 varchar(12) FALSE
SecuAbbr 证券名称 nvarchar(40) TRUE
ReportPeriod 报告期 datetime TRUE
TradeDate 交易日期 datetime FALSE
PE1 市盈率(TTM) numeric(14,4) 倍 TRUE 市盈率(TTM)
PB 市净率 numeric(14,4) 倍 TRUE
PS 市销率 numeric(14,4) 倍 TRUE
ClosePrice 最新收盘价 numeric(12,4) 元 TRUE
RMBShares 最新流通A股 numeric(18,4) 万股 TRUE
ValidShares 最新有效流通股 numeric(18,4) 万股 TRUE 扣除了非有效流通股数据
MarketValue 最新总市值 numeric(18,4) 元 TRUE
'''
class ValueDaily():
def __init__(self):
pass
'''
def __init__(self,SecuCode,SecuAbbr,TradeDate,ReportPeriod,PE1,PB,PS,ClosePrice,RMBShares,Bshares,ValidShares,MarketValue):
self.SecuCode = SecuCode
self.SecuAbbr = SecuAbbr
self.TradeDate = TradeDate
self.ReportPeriod = ReportPeriod
self.PE1 = PE1
self.PB = PB
self.PS = PS
self.ClosePrice = ClosePrice
self.RMBShares = RMBShares
self.ValidShares = ValidShares
self.MarketValue = MarketValue
'''
def printMe(self):
print(self.SecuCode,self.SecuAbbr,self.TradeDate)
\ No newline at end of file
######################################################################################################################
'''
列名 中文名 别名 类型 单位 默认值 是否可空 说明
SecuCode 证券代码 varchar(10) FALSE
SecuAbbr 证券简称 nvarchar(40) TRUE
SecuMarket 证券市场 varchar(12) TRUE
TradeDate 交易日期 datetime FALSE
TradeStatus 交易状态 char(1) TRUE
PreClosePrice 昨收 numeric(20,4) 元 TRUE
OpenPrice 今开盘 numeric(20,4) 元 TRUE
ClosePrice 今收盘 numeric(20,4) 元 TRUE
HighPrice 最高价 numeric(20,4) 元 TRUE
LowPrice 最低价 numeric(20,4) 元 TRUE
TurnoverVolume 成交量 numeric(20,4) 手 TRUE
TurnoverValue 成交额 numeric(20,4) 元 TRUE
TurnoverDeals 成交笔数 numeric(20,4) 笔 TRUE
Amplitude 振幅 numeric(20,4) % TRUE
ChangeRatio 日涨跌幅 numeric(20,4) % TRUE
FlowShares 流通股本 numeric(30,4) TRUE
FlowMarketValue 流通市值 numeric(30,4) TRUE
TotalEquity 公司总股本 numeric(20,4) TRUE
IssueEquity 发行总股本 numeric(30,4) TRUE
TotalValue 总市值 numeric(30,4) TRUE
RecordID 记录ID uniqueidentifier (newid()) TRUE
RiseOrDown 涨跌标示 varchar(2) TRUE 1:涨停 -1:跌停 0:无涨跌停
'''
class Stock():
def __init__(self):
pass
'''
def __init__(self,SecuCode,SecuAbbr,SecuMarket,TradeDate,TradeStatus,PreClosePrice,OpenPrice,ClosePrice,HighPrice,
LowPrice,TurnoverVolume,TurnoverValue,TurnoverDeals,Amplitude,ChangeRatio,FlowShares,FlowMarketValue,TotalEquity,IssueEquity,
TotalValue,RecordID,LLimitUpTime,RiseOrDown):
self.SecuCode = SecuCode
self.SecuAbbr = SecuAbbr
self.SecuMarket = SecuMarket
self.TradeDate = TradeDate
self.TradeStatus = TradeStatus
self.PreClosePrice = PreClosePrice
self.OpenPrice = OpenPrice
self.ClosePrice = ClosePrice
self.HighPrice = HighPrice
self.LowPrice = LowPrice
self.TurnoverVolume = TurnoverVolume
self.TurnoverVal = TurnoverValue
self.TurnoverDeals = TurnoverDeals
self.Amplitude = Amplitude
self.ChangeRatio = ChangeRatio
self.FlowShares = FlowShares
self.FlowMarketValue = FlowMarketValue
self.TotalEquity = TotalEquity
self.IssueEquity = IssueEquity
self.TotalValue = TotalValue
self.RecordID = RecordID
self.LLimitUpTime = LLimitUpTime
self.RiseOrDown = RiseOrDown
'''
def printMe(self):
print('this is stock!')
print(self.SecuCode,self.SecuAbbr,self.TradeDate)
########################################################################################################################
'''
EMMIDD2.dbo.CY_ValueDaily_Quote
列名 中文名 别名 类型 单位 默认值 是否可空 说明
SecuCode 证券代码 varchar(12) FALSE
SecuAbbr 证券名称 nvarchar(40) TRUE
ReportPeriod 报告期 datetime TRUE
TradeDate 交易日期 datetime FALSE
PE1 市盈率(TTM) numeric(14,4) 倍 TRUE 市盈率(TTM)
PB 市净率 numeric(14,4) 倍 TRUE
PS 市销率 numeric(14,4) 倍 TRUE
ClosePrice 最新收盘价 numeric(12,4) 元 TRUE
RMBShares 最新流通A股 numeric(18,4) 万股 TRUE
ValidShares 最新有效流通股 numeric(18,4) 万股 TRUE 扣除了非有效流通股数据
MarketValue 最新总市值 numeric(18,4) 元 TRUE
'''
class ValueDaily():
def __init__(self):
pass
'''
def __init__(self,SecuCode,SecuAbbr,TradeDate,ReportPeriod,PE1,PB,PS,ClosePrice,RMBShares,Bshares,ValidShares,MarketValue):
self.SecuCode = SecuCode
self.SecuAbbr = SecuAbbr
self.TradeDate = TradeDate
self.ReportPeriod = ReportPeriod
self.PE1 = PE1
self.PB = PB
self.PS = PS
self.ClosePrice = ClosePrice
self.RMBShares = RMBShares
self.ValidShares = ValidShares
self.MarketValue = MarketValue
'''
def printMe(self):
print(self.SecuCode,self.SecuAbbr,self.TradeDate)
###########################################################################################################################################
'''
列名 中文名 别名 类型 单位 默认值 是否可空 说明
SecuCode 证券代码 varchar(10) FALSE
SecuAbbr 证券简称 nvarchar(40) TRUE
TradeDate 交易日期 datetime FALSE
TradeStatus 交易状态 char(1) TRUE
PreClosePrice 昨收 numeric(20,4) 元 TRUE
OpenPrice 今开盘 numeric(20,4) 元 TRUE
ClosePrice 今收盘 numeric(20,4) 元 TRUE
HighPrice 最高价 numeric(20,4) 元 TRUE
LowPrice 最低价 numeric(20,4) 元 TRUE
TurnoverVolume 成交量 numeric(20,4) 手 TRUE
TurnoverValue 成交额 numeric(20,4) 元 TRUE
TurnoverDeals 成交笔数 numeric(20,4) 笔 TRUE
ChangeRatio 日涨跌幅 numeric(20,4) % TRUE
'''
class Index():
def __init__(self):
pass
'''
def __init__(self,SecuCode,SecuAbbr,SecuMarket,TradeDate,TradeStatus,PreClosePrice,OpenPrice,ClosePrice,HighPrice,
LowPrice,TurnoverVolume,TurnoverValue,TurnoverDeals,Amplitude,ChangeRatio,FlowShares,FlowMarketValue,TotalEquity,IssueEquity,
TotalValue,RecordID,RiseOrDown):
self.SecuCode = SecuCode
self.SecuAbbr = SecuAbbr
self.TradeDate = TradeDate
self.TradeStatus = TradeStatus
self.PreClosePrice = PreClosePrice
self.OpenPrice = OpenPrice
self.ClosePrice = ClosePrice
self.HighPrice = HighPrice
self.LowPrice = LowPrice
self.TurnoverVolume = TurnoverVolume
self.TurnoverValue = TurnoverValue
self.TurnoverDeals = TurnoverDeals
self.ChangeRatio = ChangeRatio
'''
def printMe(self):
print(self.SecuCode,self.SecuAbbr,self.TradeDate)
###########################################################################################################################################
'''
EMMIDD2.dbo.CY_FinacialRatio_RP
SecuCode 证券代码 varchar(12) FALSE
SecuAbbr 证券名称 nvarchar(40) TRUE
EndDate 报告日期 datetime FALSE
PublDate 公告日期 datetime TRUE
InventoryTurnover 存货周转率 numeric(18,4) 次 TRUE
AccountsReceivableTurnover 应收账款周转率 numeric(18,4) 次 TRUE
FixedAssetsTurnover 固定资产周转率 numeric(18,4) 次 TRUE
OperatingRevenueYoY 主营收入同比 numeric(18,4) TRUE
DeductNetProfitYoY 归属于母公司股东的扣除非经常性损益后的净利润同比 numeric(18,4) TRUE
'''
class FinancialRatioRP():
def __init__(self):
pass
'''
def __init__(self,SecuCode,SecuAbbr,TradeDate,ReportPeriod,PE1,PB,PS,ClosePrice,RMBShares,Bshares,ValidShares,MarketValue):
self.SecuCode = SecuCode
self.SecuAbbr = SecuAbbr
self.EndDate = EndDate
self.PublDate = PublDate
self.InventoryTurnover = InventoryTurnover
self.AccountsReceivableTurnover = AccountsReceivableTurnover
self.FixedAssetsTurnover = FixedAssetsTurnover
self.OperatingRevenueYoY = OperatingRevenueYoY
self.DeductNetProfitYoY = DeductNetProfitYoY
'''
def printMe(self):
print(self.SecuCode,self.SecuAbbr,self.EndDate,self.DeductNetProfitYoY)
##########################################################################################################################
'''
EMMIDD2.dbo.CY_FinacialIndicators_RP
列名 中文名 别名 类型 单位 默认值 是否可空 说明
SecuCode 证券代码 varchar(12) FALSE
SecuAbbr 证券名称 nvarchar(40) TRUE
EndDate 报告日期 datetime FALSE
TotalAssets 总资产 numeric(18,4) 元 TRUE
AdvanceReceipts 预收款项 numeric(18,4) TRUE
EquityBelongedToPC 净资产 numeric(18,4) TRUE
PublDate 公告日期 datetime TRUE
TotalLiability 总负债 numeric(18,4) 元 TRUE
TotalCurrentLiability 流动负债 numeric(18,4) 元 TRUE
TotalCurrentAssets 流动资产 numeric(18,4) 元 TRUE
CashOrDepositInCentralBank 现金及存放中央银行款项 numeric(18,4) 元 TRUE
NetProfit 净利润 numeric(18,4) 元 TRUE
NetOperateCashFlow 经营活动产生的现金流 numeric(18,4) 元 TRUE
'''
class FinancialIndicator():
def __init__(self):
pass
'''
def __init__(self,SecuCode,SecuAbbr,TradeDate,ReportPeriod,PE1,PB,PS,ClosePrice,RMBShares,Bshares,ValidShares,MarketValue):
self.SecuCode = SecuCode
self.SecuAbbr = SecuAbbr
self.EndDate = EndDate
self.PublDate = PublDate
self.TotalCurrentAssets = TotalCurrentAssets
self.TotalAssets = TotalAssets
self.AdvanceReceipts = AdvanceReceipts
self.EquityBelongedToPC = EquityBelongedToPC
self.CashOrDepositInCentralBank = CashOrDepositInCentralBank
self.TotalLiability = TotalLiability
self.TotalCurrentLiability = TotalCurrentLiability
self.NetProfit = NetProfit
self.NetOperateCashFlow = NetOperateCashFlow
'''
def printMe(self):
print(self.SecuCode,self.SecuAbbr,self.EndDate)
'''
myBT = engine(portforlio,money,startDate,endDate,turnover,benchmark,price)
myBT.backTesting()
myBT.getDailyResult()
'''
import logging
import sys
import os
logger = logging.getLogger(__name__)
logger.setLevel(logging.CRITICAL) # higher level, less showing
twoLevelUp = os.path.abspath(os.path.join(__file__ ,"../.."))
oneLevelUp = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0,twoLevelUp)
#print(sys.path)
from util.kySDK import *
from ..API.highway import highwayAPI
from .position import Position
from .account import Account
from datetime import datetime
from dataTable import DataTable
import os
import time
import sys
import pyodbc
import json
class Engine():
myAPI = highwayAPI()
myAPI.init()
def __str__(self):
print('This is a backtesting engine')
def __init__(self,inputJson):
'''
ID = '1'
strategy = \
{
"stockPicker":{'pick':False,'dateList':None},
"criteriaDict" : {"PE(TTM)":["PE","CY_ValueDaily_Quote"]},
"portforlio": ['600000','000002'],
"benchmark": 'HS300',
"money": 5000000,
"startDate": '2014-07-01',
"endDate": '2016-02-01',
"turnover": 10,
"tradingHabit": "OpenPrice",
"status":0
}
inputJson = \
{
ID:strategy
}
'''
self.inputJson = inputJson
self.strategyID = inputJson['strategyID']
self.stockPicker = inputJson['stockPicker']
self.criteriaDict = inputJson['criteriaDict']
portforlio = inputJson['portforlio']
self.benchmark = inputJson['benchmark']
self.initialCash = inputJson['money']
self.startDate = inputJson['startDate']
self.endDate = inputJson['endDate']
self.turnover = inputJson['turnover']
self.tradingHabit = inputJson['tradingHabit']
self.status = inputJson['status']
logger.critical('\n开始运行策略:%s'%self.strategyID)
if self.stockPicker['pick'] == True:
if isinstance(self.stockPicker['dateList'],list) is not True:
raise ValueError("['stockPicker'][bool,list of date]")
if False in [True if self.endDate >= x and x>= self.startDate else False for x in self.stockPicker['dateList'] ]:
raise ValueError("['stockPicker'][bool,list of date], stockPicking list of date should be within startDate and endDate")
if self.endDate < self.startDate:
raise ValueError('endDate cannot be smaller than startDate')
if self.endDate >= self.myAPI.getYesterday():
self.endDate = self.myAPI.getYesterday()
if self.turnover == 1:
raise ValueError('没有T+0,-买-持-卖-整个周期不可能是1天')
if self.turnover == 0:
self.turnover = 10000000
if isinstance(portforlio,str) and portforlio in ['HS300','ZZ500','SZ50']:
if portforlio == 'HS300':
portforlio = 'SHSE.000300'
elif portforlio == 'ZZ500':
portforlio = 'SHSE.000905'
elif portforlio == 'SZ50':
portforlio = 'SHSE.000016'
self.portforlio = [stockCode[-6:] for stockCode in getIndexPoolFromKy(portforlio)] #['SHSE.600000','SHSE.000002']
elif isinstance(portforlio,str) and portforlio == 'all':
self.portforlio = self.myAPI.getAllStockCode(self.startDate)
elif isinstance(portforlio,list):
self.portforlio = portforlio
print('len(self.portforlio)',len(self.portforlio))
# download and process all data needed
self.downloadA = time.time()
self.bulk = self.myAPI.getAllNeeded(self.inputJson)
self.downloadB = time.time()
#print('self.bulk: ',self.bulk)
self.BTRecords = dict()
self.IndexRecords = None
self.tradingDayHistory = list() # will help get the previous trading day
@staticmethod
def findRightDate(oneDay,option):
'''
根据oneDay和option找出最近的一期(几期)财报
option = '最近三年','最近一年','最近一期'
比如
oneDay = '2014-12-30'
option = '最近三年'
则返回 ['2013-12-31','2012-12-31','2011-12-30']
'''
reportDateList = ['03-31','06-30','09-30','12-31']
#fisrtYear = oneDay[:4]
#recentMonthDay = oneDay[5:]
#recentReportDate = recentYear + '-' + [x for x in reportDateList if oneDay[5:] > x][0]
if option == '最近三年':
if '12-31' not in oneDay:
firstYear = str(eval('int(oneDay[:4]) - 1')) + '-' + '12-31'
secondYear = str(eval('int(oneDay[:4]) - 2')) + '-' + '12-31'
thirdYear = str(eval('int(oneDay[:4]) - 3')) + '-' + '12-31'
fourthYear = str(eval('int(oneDay[:4]) - 4')) + '-' + '12-31' # 多加一年,保险起见
else:
firstYear = oneDay
secondYear = str(eval('int(firstYear[:4]) - 1')) + '-' + '12-31'
thirdYear = str(eval('int(firstYear[:4]) - 2')) + '-' + '12-31'
fourthYear = str(eval('int(firstYear[:4]) - 3')) + '-' + '12-31'
return [firstYear,secondYear,thirdYear,fourthYear]
elif option == '最近一年':
if '12-31' not in oneDay:
firstYear = str(eval('int(oneDay[:4]) - 1')) + '-' + '12-31'
secondYear = str(eval('int(oneDay[:4]) - 2')) + '-' + '12-31'
else:
firstYear = oneDay
secondYear = str(eval('int(firstYear[:4]) - 1')) + '-' + [x for x in reportDateList if firstYear[5:] > x][0]
return [firstYear,secondYear]
elif option == '最近一期':
if oneDay[5:] not in reportDateList:
if oneDay[5:] < reportDateList[0]: #'01-02' < '03-31'
firstYear = str(eval('int(oneDay[:4]) - 1'))
firstPeriod = firstYear + '-' + reportDateList[-1]
secondPeriod = firstYear + '-' + reportDateList[-2]
elif oneDay[5:] < reportDateList[1]: #'04-05' < '06-30'
firstYear = oneDay[:4]
firstPeriod = firstYear + '-' + reportDateList[0]
secondPeriod = str(eval('int(firstYear) - 1'))+ '-' + reportDateList[3]
else:
firstYear = oneDay[:4]
#print('test: ',[x for x in reportDateList if oneDay[5:] > x])
firstPeriod = firstYear + '-' + [x for x in reportDateList if oneDay[5:] > x][-1]
secondPeriod = firstYear + '-' + [x for x in reportDateList if oneDay[5:] > x][-2]
else: #oneDay = '2015-03-31' or '2016-12-31'
if oneDay[5:] == '03-31':
firstPeriod = oneDay
secondPeriod = str(eval('int(oneDay[:4]) - 1')) + '-' + '12-31'
else:
firstPeriod = oneDay
secondPeriod = oneDay[:4] + '-' + [x for x in reportDateList if oneDay[5:] > x][-1]
return [firstPeriod,secondPeriod]
else:
raise ValueError('输入要是最近一期,最近一年,最近三年,请检查Json的输入')
def extractBulk(self,oneDay):
'''
oneDay = '2013-05-03' #'2014-12-30'
extract from myAPI.getAllNeeded(inputJson)
{'EM_QUOTE_STOCK_DAILY':{date:[stockObj]},
'CY_FinacialRatio_RP':{date:[financialRatioObj]},
return is
{'600485': {'主营收入同比': [Decimal('13.2225'), Decimal('33.8532'), Decimal('851.6680')]},
'600000': {'主营收入同比': [Decimal('18.9713'), Decimal('18.9713'), Decimal('23.1625'), Decimal('23.1625'), Decimal('20.5
697'), Decimal('20.5697')]},
'000002': {'主营收入同比': [Decimal('33.5828'), Decimal('8.1002'), Decimal('31.3263')]}}
'''
resultDict = dict()
for stock in self.portforlio:
resultDict[stock] = dict()
for criteria in self.inputJson['criteriaDict']:
#print('extractBulk: ',self.inputJson[self.strategyID]['criteriaDict'][criteria][0])
if self.inputJson['criteriaDict'][criteria][0] == '最近三年':
DateList = self.findRightDate(oneDay,self.inputJson['criteriaDict'][criteria][0])
firstYear = DateList[0]
secondYear = DateList[1]
thirdYear = DateList[2]
#找到相对应的criteria数据
criteriaList = []
for year in [firstYear,secondYear,thirdYear]:
tableName = DataTable[criteria][1]
matchedObj = [x for x in self.bulk[tableName][year] if x.SecuCode == stock and x.EndDate == year]
if not matchedObj:
oneObj = matchedObj[0] if len(matchedObj) > 1 else matchedObj[0] # 可能重复,需要去重,这里是简单地取第一个
criteriaList.append(eval('oneObj.%s'%(DataTable[criteria][0])))
'''
for finObj in bulk['CY_FinacialRatio_RP'][year]:
#print(finObj.SecuCode,finObj.EndDate)
if finObj.SecuCode == stock and finObj.EndDate == year:
criteriaList.append(eval('finObj.%s'%(DataTable[criteria][0])))
'''
resultDict[stock][criteria] = criteriaList
elif self.inputJson['criteriaDict'][criteria][0] == '最近一年':
DateList = self.findRightDate(oneDay,self.inputJson['criteriaDict'][criteria][0])
'''
for spotDate in list(bulk['CY_FinacialRatio_RP'].keys()): #dict_keys(['2015-12-31', '2015-09-30', '2015-06-30', '2015-03-31', '2014-12-31'])
if '-12-31' in spotDate:
firstYear = spotDate #'2015-12-31'
break
'''
firstYear = DateList[0] #还没有考虑
#找到相对应的criteria数据
criteriaList = []
#print(self.bulk['CY_FinacialRatio_RP'])
tableName = DataTable[criteria][1]
try:
matchedObj = [x for x in self.bulk[tableName][firstYear] if x.SecuCode == stock and x.EndDate == firstYear]
except KeyError:
matchedObj = [x for x in self.bulk[tableName][DateList[1]] if x.SecuCode == stock and x.EndDate == DateList[1]]
if matchedObj:
oneObj = matchedObj[0] if len(matchedObj) > 1 else matchedObj[0] # 可能重复,需要去重,这里是简单地取第一个
criteriaList.append(eval('oneObj.%s'%(DataTable[criteria][0])))
'''
for finObj in bulk['CY_FinacialRatio_RP'][firstYear]:
#print(finObj.SecuCode,finObj.EndDate)
if finObj.SecuCode == stock and finObj.EndDate == firstYear:
criteriaList.append(eval('finObj.%s'%(DataTable[criteria][0])))
'''
resultDict[stock][criteria] = criteriaList
elif self.inputJson['criteriaDict'][criteria][0] == '最近一期':
DateList = self.findRightDate(oneDay,self.inputJson['criteriaDict'][criteria][0])
firstPeriod = DateList[0]
#找到相对应的criteria数据
criteriaList = []
tableName = DataTable[criteria][1]
try:
matchedObj = [x for x in self.bulk[tableName][firstYear] if x.SecuCode == stock and x.EndDate == firstYear]
except KeyError:
matchedObj = [x for x in self.bulk[tableName][DateList[1]] if x.SecuCode == stock and x.EndDate == DateList[1]]
if matchedObj:
oneObj = matchedObj[0] if len(matchedObj) > 1 else matchedObj[0] # 可能重复,需要去重,这里是简单地取第一个
criteriaList.append(eval('oneObj.%s'%(DataTable[criteria][0])))
'''
for finObj in bulk['CY_FinacialRatio_RP'][firstPeriod]:
#print(finObj.SecuCode,finObj.EndDate)
if finObj.SecuCode == stock and finObj.EndDate == firstPeriod:
criteriaList.append(eval('finObj.%s'%(DataTable[criteria][0])))
'''
resultDict[stock][criteria] = criteriaList
else:
raise ValueError('输入要是最近一期,最近一年,最近三年,请检查Json的输入')
#print('extractBulk: ',resultDict,)
return resultDict
def screenStock(self,resultDict,oneDay):
"""
{'600485': {'主营收入同比': [Decimal('13.2225'), Decimal('33.8532'), Decimal('851.6680')]},
'600000': {'主营收入同比': [Decimal('18.9713'), Decimal('18.9713'), Decimal('23.1625'), Decimal('23.1625'), Decimal('20.5
697'), Decimal('20.5697')]},
'000002': {'主营收入同比': [Decimal('33.5828'), Decimal('8.1002'), Decimal('31.3263')]}}
{'600485':True,
'600000':False,
...
}
"""
screenResult = dict()
for stock,value in resultDict.items():
multiple = []
for criteria,valueList in value.items():
upperbound = self.inputJson['criteriaDict'][criteria][1]
lowerbound = self.inputJson['criteriaDict'][criteria][2]
if upperbound == None:
upperbound = float('inf')
if lowerbound == None:
lowerbound = -float('inf')
if len(valueList) == 0:
single = False
else:
# 全真则为真,否则为假
single = False if False in [True if x > lowerbound and x < upperbound else False for x in valueList] else True #[Decimal('13.2225'), Decimal('33.8532'), Decimal('851.6680')]
multiple.append(single)
consilidatedResult = False if False in multiple else True
screenResult[stock] = consilidatedResult
#print('screenStock: ',screenResult)
return screenResult
@staticmethod
def getTrueStock(screenResult):
mylist = []
for key,value in screenResult.items():
if value == True:
mylist.append(key)
#print('getTrueStock: ',mylist)
return mylist
@staticmethod
def printRecord(mydict):
#print('mydict: ',mydict)
for x in mydict:
print(x,mydict[x])
def keepRecord(self,DBcnxn,oneDay,positionList,totalValue,cash,positionListValue):
'''
put daily BT record into Json
'''
if oneDay == self.startDate:
self.status = 0
elif oneDay == self.endDate:
self.status = 2
else:
self.status = 1
#time.sleep(0.1)
self.BTRecords[oneDay] = dict()
self.BTRecords[oneDay]['positionList'] = positionList
self.BTRecords[oneDay]['totalValue'] = totalValue
self.BTRecords[oneDay]['cash'] = cash
self.BTRecords[oneDay]['positionListValue'] = positionListValue
self.tradingDayHistory.append(oneDay)
'''
if not os.path.exists('./Strategy Records'):
os.makedirs('./Strategy Records')
with open('./Strategy Records/strategy_%s.txt'%self.strategyID,'a+') as f:
f.write('--------' + oneDay + '----------\n')
f.write('positionList: %s\n'%len(positionList))
f.write('cash: %s\n'%cash)
f.write('positionListValue: %s\n'%positionListValue)
f.write('totalValue: %s\n'%totalValue)
f.write('\n')
'''
insertCommand = "INSERT INTO BT.dbo.BTrecords VALUES (%s,%i,'%s','%s',%i,'%s',%i,%0.2f,%0.2f,%0.2f,'%s');"%(
self.strategyID,self.status,self.startDate,self.endDate,self.length,
oneDay,len(positionList),cash,positionListValue,totalValue,json.dumps(self.tradingDayAPI))
#print(insertCommand)
try:
DBcnxn.cursor().execute(insertCommand)
DBcnxn.commit()
except:
DBcnxn.rollback()
def getRecord(self,oneDay):
return self.BTRecords[oneDay]
def stockPicking(self,oneDay):
if self.stockPicker['pick'] == True:
logger.critical('################################ stockPicking ###################################: %s'%(self.strategyID))
resultDict = self.extractBulk(oneDay)
screenDict = self.screenStock(resultDict,oneDay)
screenStocks = self.getTrueStock(screenDict)
return screenStocks
def getMarketDailyValue(self,oneDay,stockList):
return [obj for obj in self.bulk['EM_QUOTE_STOCK_DAILY'][oneDay] if obj.SecuCode in stockList]
def backTesting(self):
self.tradingDayAPI = self.myAPI.getTradingDays(self.startDate,self.endDate)
self.length = len(self.tradingDayAPI)
a = time.time()
DBcnxn = pyodbc.connect(DSN = 'TEST',autocommit=True)
DBcursor = DBcnxn.cursor()
createDBcommand = \
'''
if not exists(select * from sys.databases where name = N'BT')
BEGIN
CREATE database BT;
END
'''
createTableCommand = """
IF NOT EXISTS (SELECT * FROM BT.INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'BTrecords')
begin
create table BT.[dbo].BTrecords(
strategyID VARCHAR(255),
status VARCHAR(255),
startDate VARCHAR(255),
endDate VARCHAR(255),
length VARCHAR(255),
btdate VARCHAR(255),
positionList VARCHAR(255),
cash VARCHAR(255),
positionListValue VARCHAR(255),
totalValue VARCHAR(255),
tradingDayAPI VARCHAR(8000));
end
"""
#DBcursor.execute(createCommand)
dropTableCommand = """
IF EXISTS (SELECT * FROM BT.INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'BTrecords')
begin
drop table BT.[dbo].BTrecords;
end
"""
#DBcursor.execute(dropCommand)
DBcursor.execute(createDBcommand + createTableCommand )
logger.critical('################################ backtesting ###################################: %s'%(self.strategyID))
self.myaccount = Account(self.initialCash,'OpenPrice')
'''
tradingHabit: 'OpenPrice'|'ClosePrice'| OpenPrice is prefered
criteriaDict: {'DeductNetProfitYoY':[None,5,'最近一期']} #dict(item:[period,upperbound,lowerbound])
if turnover is int, then turnover for every N days
if turnover is None, then buy and sell according to specific condition,
if turnover is flexible, then buy and sell according to specific condition, + turnover days
'''
#time.sleep(2)
#print('aaaaaaaaaaaaaaaaaaaaaa','./Strategy Records/strategy_%s.txt'%(self.strategyID))
'''
try:
with open('./Strategy Records/strategy_%s.txt'%self.strategyID,'w+') as f:
f.write(' |' + self.strategyID + '| \n')
except:
e = sys.exc_info()[0]
print( "Error: %s" % e )
'''
if isinstance(self.turnover,int) and self.turnover > 0: # keep turnover
self.totalRuningDays = 1
turnoverDays = 0 #this one is crucial! 0 instead of 1
self.sold = self.bought = False
for day in self.myAPI.getTradingDays(self.startDate,self.endDate):
logger.critical('%s ########## %s ############ totalRuningDays %s'%(self.strategyID,day,self.totalRuningDays))
self.myaccount.printMe()
self.keepRecord(DBcnxn,day,self.myaccount.positionList,self.myaccount.totalValue,self.myaccount.cash,self.myaccount.positionListValue) # keep daily BT record
previousPositionList = None
if len(self.tradingDayHistory) > 1:
previousTradingDay = self.tradingDayHistory[-2]
previousRecord = self.getRecord(previousTradingDay)
previousPositionList = previousRecord['positionList']
###############
#print('bought: %s, sold: %s,turnoverDays: %d'%(str(self.bought),str(self.sold),turnoverDays))
if turnoverDays < self.turnover:
if self.bought == False:
self.executeBuy(day,previousPositionList)
if self.bought == True: #如果当日成功买入,turnoverDays 才会变化
turnoverDays += 1
else:
self.executeHeld(day)
turnoverDays += 1
self.totalRuningDays += 1
elif turnoverDays == self.turnover:
self.executeSell(day)
if self.sold == True: # 如当日成功卖出去了,才会买入,turnoverDays才会变化,如当日没有成功卖出去了,第二天继续试着卖
self.executeBuy(day,previousPositionList)
turnoverDays = 1
self.totalRuningDays += 1
elif self.turnover is None:
#if stock.SecuCode == '600000' and round(stock.OpenPrice)%2 == 1:
pass
elif self.turnover == 'flexible':
pass
self.getIndexResult()
logger.critical('######################### backtesting completed ############ strategyID: %s, 下载耗时 %0.1f秒, 运行耗时 %0.1f秒'%
(self.strategyID,self.downloadB-self.downloadA,time.time()-a))
def executeHeld(self,day):
print('持有更新')
heldPosition = self.myaccount.positionList
try:
updatedStock = self.getMarketDailyValue(day,[x.SecuCode for x in heldPosition])
except:
updatedStock = []
if len(updatedStock) != len(heldPosition):
print(
'''
昨日持仓的数目,今天想更新,但是近日的股票数目少了,数据问题
昨今持仓差:''',(set(x.SecuCode for x in heldPosition) - set(x.SecuCode for x in updatedStock)))
# this is bad, the remedy is to use yesterday's position to pretend as today's stockBar
for p in heldPosition:
for SecuCode in set(x.SecuCode for x in heldPosition) - set(x.SecuCode for x in updatedStock):
if p.SecuCode == SecuCode:
updatedStock.append(p)
#print('hehe',[x.TradeStatus for x in updatedStock])
#print('haha',[x.PreClosePrice for x in updatedStock])
temp = self.myaccount.held(day,updatedStock)
if isinstance(temp,str):
return
def executeSell(self,day):
print('卖出')
if self.myaccount.isempty():
logger.warn('empty nothing to sell in this account')
self.sold = False
return None
#卖掉
positionStock = self.myaccount.positionList # get positions in account and sell
try:
marketValueOneDay = self.getMarketDailyValue(day,[x.SecuCode for x in positionStock])
except:
marketValueOneDay = []
if len(positionStock) != len(marketValueOneDay):
logger.warn(
'''
昨日持仓的数目,今天想更新,但是近日的股票数目少了,数据问题
昨今持仓差:''',(set(x.SecuCode for x in positionStock) - set(x.SecuCode for x in marketValueOneDay)))
# this is bad, the remedy is to use yesterday's position to pretend as today's stockBar
for p in positionStock:
for SecuCode in set(x.SecuCode for x in positionStock) - set(x.SecuCode for x in marketValueOneDay):
if p.SecuCode == SecuCode:
marketValueOneDay.append(p)
logger.warn(marketValueOneDay)
for stock in list(marketValueOneDay):
#print(stock.TradeStatus)
#print('stock.LLimitUpTime',stock.LLimitUpTime,' ',type(stock.LLimitUpTime)) # exclude Tingpai or LimitUp before the market is open
if stock.TradeStatus == '0':
logger.warn('TradeStatus = "0" , 当日交易被禁止,从卖出列表中删除之,: %s'%stock.SecuCode)
marketValueOneDay.remove(stock)
if stock.RiseOrDown == '-1' and datetime.strptime(stock.LLimitUpTime[11:],'%H:%M:%S') < datetime(1900, 1, 1, 9, 30,0):
logger.warn('9:30以前跌停,无法卖出,从卖出列表中删除之') # 还没有考虑之后打开跌停的情况
marketValueOneDay.remove(stock)
if marketValueOneDay is None: #if marketValueOneDay has no data
logger.warn('%s has no data, will sell the next day'%day)
self.sold = False
return
self.myaccount.sellAvailableForSell([x.SecuCode for x in marketValueOneDay])
self.sold = True
def executeBuy(self,day,previousPositionList):
trueStocks = self.stockPicking(day)
print('买入')
# update
if len(trueStocks) == 0:
self.bought = False
logger.warn('%s has no data, continue'%day)
return
try:
marketValueOneDay = self.getMarketDailyValue(day,trueStocks)
except:
marketValueOneDay = []
updatedStock = list(marketValueOneDay)
logger.warn(marketValueOneDay)
for stock in list(marketValueOneDay):
#print('stock.LLimitUpTime',stock.LLimitUpTime,' ',type(stock.LLimitUpTime)) # exclude Tingpai or LimitUp before the market is open
if stock.TradeStatus == '0':
logger.warn('TradeStatus = "0" , 当日交易被禁止,从需买列表中删除之,:%s'%stock.SecuCode)
marketValueOneDay.remove(stock)
if stock.RiseOrDown == '1' and datetime.strptime(stock.LLimitUpTime[11:],'%H:%M:%S') < datetime(1900, 1, 1, 9, 30,0):
logger.warn('9:30以前涨停,无法买入,从需买列表中删除之')
marketValueOneDay.remove(stock)
if previousPositionList:
if stock.SecuCode in [x.SecuCode for x in previousPositionList]:
logger.warn('昨日的positionList中已有该票,无法买入,从需买列表中删除之: %s'%stock.SecuCode)
if stock.SecuCode in [x.SecuCode for x in marketValueOneDay]:
marketValueOneDay.remove(stock)
if len(marketValueOneDay) == 0: #if marketValueOneDay has no data,
logger.warn('%s 今日无可买入票,明日继续买;如有持仓,今日更新持仓,'%day)
self.bought = False
temp = self.myaccount.held(day,updatedStock)
if isinstance(temp,str):
return
#print('len(marketValueOneDay) ',len(marketValueOneDay))
for stock in marketValueOneDay: # buy the stock that is available to buy
#print('backtesting check: %s',stock.SecuCode)
#print('len(marketValueOneDay) ',len(marketValueOneDay))
#print('myaccount.cash ',myaccount.cash)
self.myaccount.buy(stock,self.myaccount.initialCash*0.95/len(marketValueOneDay)) # need to be very careful, make sure the initialCash won't change constantly
self.bought = True
def getIndexResult(self):
indexList = [self.bulk['EM_QUOTE_INDEX_DAILY'][x][0] for x in self.bulk['EM_QUOTE_INDEX_DAILY']]
indexList.reverse()
#print('indexList[0].TradeDate,self.startDate: ',indexList[0].TradeDate,self.startDate)
#print('indexList',indexList)
#print(indexList[-1].TradeDate,' ', indexList[-1].ClosePrice,' ', indexList[0].TradeDate,' ', indexList[0].ClosePrice)
#zhangfu1 = (indexList[-1].ClosePrice - indexList[0].ClosePrice ) / indexList[0].ClosePrice
#print('zhangfu of index', zhangfu1)
self.IndexRecords = indexList
#with open('result.txt','a+') as f:
# f.write(str(zhangfu1) + str(datetime.now()) + '\n')
#self.printRecord(self.BTRecords)
\ No newline at end of file
# -*- coding: utf-8 -*-
"""
Created on 20180313
@author: Matthew
"""
import numpy as np
from matplotlib import pyplot as plt
import datetime
import matplotlib.pyplot as plt
from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['SimHei']
#mpl.rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 指定默认字体:解决plot不能显示中文问题
mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
def anualizedreturn(netvalue):
'''
年化收益
'''
myreturn = ( netvalue[-1] - netvalue[0] ) / netvalue[0]
annualReturn = str( round( ((1+myreturn)**(250/len(netvalue)) - 1) * 100 , 2)) + '%'
print( '年化收益为 %s'%annualReturn)
return annualReturn
def growthPercent(netvalue):
'''
策略涨幅
'''
print('期间涨幅为 ' + str( round( ( netvalue[-1] - netvalue[0] ) / netvalue[0] * 100 , 2)) + '%')
return str( round( ( netvalue[-1] - netvalue[0] ) / netvalue[0] * 100 , 2)) + '%'
def maxdrawdown(netvalue):
i = np.argmax( (np.maximum.accumulate(netvalue) - netvalue ) / np.maximum.accumulate(netvalue) ) # end of the period
#print('i:', i)
j = np.argmax( netvalue[:i] )
drawdown = ( 1 - netvalue[i] / netvalue[j] )
print('最大回撤为 ' + str(round(drawdown*100,2)) + '%')
return str(round(drawdown*100,2)) + '%'
def maxdrawdown2(netvalue):
'''
我的方法更实用
最大回撤,当前点,历史最高点,
如果当前点在历史最高点以下,算出之间的降幅,只保留最大的那个降幅
如果当前点在历史最高点以上,不做任何计算,
'''
index = 0
drawdown = 0
for dot in netvalue:
highestDot = max(netvalue[:index]) if index is not 0 else netvalue[0]
if dot >= highestDot:
index += 1
continue
elif dot < highestDot and (dot - highestDot)/highestDot < drawdown:
drawdown = (dot - highestDot)/highestDot
index += 1
print('最大回撤为 ' + str(round(drawdown*100,2)) + '%')
return str(round(drawdown*100,2)) + '%'
def evaluator(strategyID,BTRecords,indexRecords,plot):
'''
indexRecords: list of index objs
self.BTRecords[oneDay] = dict()
self.BTRecords[oneDay]['positionList'] = positionList
self.BTRecords[oneDay]['totalValue'] = totalValue
self.BTRecords[oneDay]['cash'] = cash
self.BTRecords[oneDay]['positionListValue'] = positionListValue
'''
BTtradingDays = []
totalValue = []
cash = []
positionListValue = []
for day,value in BTRecords.items():
BTtradingDays.append(day)
totalValue.append(value['totalValue'])
cash.append(value['cash'])
positionListValue.append(value['positionListValue'])
BTNetvalue = [x/totalValue[0] for x in totalValue]
cashNetValue = [x/cash[0] for x in cash]
# positionvalue is 0 initially
positionListNetValue = [x/cash[0] for x in positionListValue]
print('\n策略评价:')
growthPercent(BTNetvalue)
anualizedreturn(BTNetvalue)
maxdrawdown2(BTNetvalue)
print('\n同期指数评价:')
#print(type(indexRecords),indexRecords)
indexNetvalue = [x.ClosePrice/indexRecords[0].ClosePrice for x in indexRecords]
indexDate = [x.TradeDate for x in indexRecords]
growthPercent(indexNetvalue)
anualizedreturn(indexNetvalue)
maxdrawdown2(indexNetvalue)
'''
print('len(BTtradingDays): ',len(BTtradingDays),BTtradingDays[:5])
print('len(BTNetvalue): ',len(BTNetvalue))
print('len(indexDate): ',len(indexDate),indexDate[:5])
print('len(indexNetvalue): ',len(indexNetvalue))
print('difference in days: ', set(indexDate) - set(BTtradingDays))
'''
'''
print('\n写入文件:')
with open('./Strategy Records/strategy_%s.txt'%strategyID,'a+') as f:
f.write('--------' + '策略评估' + '----------\n')
f.write('期间涨幅: %s\n'%growthPercent(BTNetvalue))
f.write('年化收益: %s\n'%anualizedreturn(BTNetvalue))
f.write('最大回撤: %s\n'%maxdrawdown2(BTNetvalue))
f.write('--------' + '指数对比' + '----------\n')
f.write('期间涨幅: %s\n'%growthPercent(indexNetvalue))
f.write('年化收益: %s\n'%anualizedreturn(indexNetvalue))
f.write('最大回撤: %s\n'%maxdrawdown2(indexNetvalue))
'''
if plot == True:
dates = [datetime.datetime.strptime(x,'%Y-%m-%d') for x in BTtradingDays]
#print('%0.0f %0.0f %0.0f %0.0f %0.0f'%(len(dates),len(BTNetvalue),len(cashNetValue),len(positionListNetValue),len(indexNetvalue)))
plt.plot(dates,BTNetvalue,'r-',dates,cashNetValue,'g-',dates,positionListNetValue,'y.',dates,indexNetvalue,'b--',)
plt.ylabel('净值')
plt.xlabel('时间')
plt.axis('tight')
plt.legend(['策略','现金','持仓','指数'])
plt.grid()
plt.show()
if __name__ == '__main__':
netvalue = [1,0.5,1,2,3,1]
netvalue = [1,1,1.01,1.03,0.4,100,10]
#maxdrawdown(netvalue)
maxdrawdown2(netvalue)
# -*- coding: utf-8 -*-
"""
Created on 20180313
@author: Matthew
"""
from numpy import prod
class Position():
'''
each stock that was being bought has a position,
one stock is matched with one position,
account can have 0 to multiple positions,
one stock can remain in position for 5 days, so this position has a recored of this stock object of 5 days
it is easier this way to calculate the real growth or drop-down over a certain period.
after sold it, position is cleared, position is not in account as well
'''
def __init__(self,stock,boughtAmount,price):
self.stock = stock
self.TradeDate = stock.TradeDate
self.boughtAmount = boughtAmount
self.SecuCode = stock.SecuCode
self.boughtPrice = price
self.initialValue = boughtAmount * price
self.positionValue = self.initialValue
self.TradeStatus = stock.TradeStatus
self.PreClosePrice = stock.PreClosePrice
self.ClosePrice = stock.ClosePrice
self.RiseOrDown = stock.RiseOrDown
#entering account
self.isinAccount = True
self.remainRecord = [stock]
def leaveAccount(self):
self.isinAccont = False
self.remainRecord.clear()
def remainInAccount(self,stock,method):
'''
method 1|2 to update the Real positionValue using method 1 or 2
'''
self.remainRecord.append(stock)
#print([x.SecuCode for x in self.remainRecord])
#print([x.ClosePrice for x in self.remainRecord])
#print([type(x.TradeStatus) for x in self.remainRecord])
#print([x.PreClosePrice for x in self.remainRecord])
if method == 1:
self.updatePositionValueMethod1()
elif method == 2:
self.updatePositionValueMethod2()
def updatePositionValueMethod1(self):
'''
买入的价格是买入的总市值除以买入时候的股本,用当前总市值除以买入时候的股本,就得到今日的价格,
当然这个价格和实际的而价格肯定不一样,但是要算涨幅的话是绰绰有余了。
参考浦发银行
同花顺的价格:
除权
2015-05-05 17.08
2018-03-15 12.38
前复权
2015-05-05 10.09
2018-03-15 12.39
实际涨幅为 (12.39-10.09)/10.09 = 22.79%
益盟的复权价格:
2015-05-05 FClosePrice 119.4343
2018-03-15 FClosePrice 127.8294
涨幅是 7.029%
Method 1
2015-05-05 总股本 18653471488.0000 总市值 318601293015.0000
2018-03-15 总股本 29352079872.0000 总市值 363672269614.0000
按照Method1 涨幅为 (318601293015.0000/18653471488.0000 - 363672269614.0000/18653471488.0000) 除以 363672269614.0000/18653471488.0000
= ( 318601293015.0000 - 363672269614.0000 ) / 363672269614.0000
= 14.14%
可以看出 this is obviously Wrong.... 涨幅不是总市值的涨幅
比较准确的算法:Method2
可以知道每日的涨幅,用(Close-PreClose)PreClose,然后用买入价格*(1+涨幅)*(1+涨幅).... 算出今日的价格,然后算出整个事件窗口的涨幅,
记得要剔除无法交易的日子。这样算出来的涨幅是6.14%
记得今日的涨幅是昨收和今收的涨幅,而不是今收和今收的涨幅,所以涨幅一定是某日收盘价和基准日的昨收价格比较
'''
initial = self.remainRecord[0]
current = self.remainRecord[-1]
initialPrice = initial.TotalValue/initial.TotalEquity
currentPrice = current.TotalValue/initial.TotalEquity
#print('initialPrice.TotalValue,initialPrice.TotalEquity',initial.TotalValue,' ',initial.TotalEquity)
#print('current.TotalValue,current.TotalEquity',current.TotalValue,' ',current.TotalEquity)
#print('initialPrice,currentPrice',initialPrice,' ',currentPrice)
growth = (currentPrice - initialPrice) / initialPrice
#print('method 1 growth', growth)
self.positionValue = self.positionValue * ( 1 + growth)
def updatePositionValueMethod2(self):
'''
[12,11,13,6] just sum the changeRatio of each day, don't need to consider 除权 issue
假设所有的涨幅都按照( 今收-昨收/昨收) 来算,其实现实可能不是这样,如果今开买入,涨幅显然就不同了
'''
changeReal = []
for x in self.remainRecord[:-1]: #
#print(type(x),x.__dict__)
if x.TradeStatus == '1':
if x.PreClosePrice == 0:
changeReal.append( 1 + x.ChangeRatio )
else:
changeReal.append( 1 + (x.ClosePrice - x.PreClosePrice)/x.PreClosePrice )
#print('changeReal',changeReal)
#print([x.ChangeRatio for x in self.remainRecord])
#print('method 2 growth', growth)
#记得今日的涨幅是昨收和今收的涨幅,而不是今收和今收的涨幅,所以涨幅一定是某日收盘价和基准日的昨收价格比较
#print('changeReal',changeReal)
#print('prod(changeReal)', prod(changeReal),' len: ',len(changeReal))
self.positionValue = self.initialValue * prod(changeReal)
def updatePositionValue(self,stock,tradingHabit):
self.positionValue = self.boughtAmount * stock.OpenPrice if tradingHabit == 'OpenPrice' else self.boughtAmount * stock.ClosePrice
def getPositionValue(self):
return self.positionValue
def findRightDate(oneDay,option):
'''
根据oneDay和option找出最近的一期(几期)财报
option = '最近三年','最近一年','最近一期'
比如
oneDay = '2014-12-30'
option = '最近三年'
则返回 ['2013-12-31','2012-12-31','2011-12-30']
'''
reportDateList = ['03-31','06-30','09-30','12-31']
#fisrtYear = oneDay[:4]
#recentMonthDay = oneDay[5:]
#recentReportDate = recentYear + '-' + [x for x in reportDateList if oneDay[5:] > x][0]
if option == '最近三年':
if '12-31' not in oneDay:
firstYear = str(eval('int(oneDay[:4]) - 1')) + '-' + '12-31'
secondYear = str(eval('int(oneDay[:4]) - 2')) + '-' + '12-31'
thirdYear = str(eval('int(oneDay[:4]) - 3')) + '-' + '12-31'
fourthYear = str(eval('int(oneDay[:4]) - 4')) + '-' + '12-31' # 多加一年,保险起见
else:
firstYear = oneDay
secondYear = str(eval('int(firstYear[:4]) - 1')) + '-' + '12-31'
thirdYear = str(eval('int(firstYear[:4]) - 2')) + '-' + '12-31'
fourthYear = str(eval('int(firstYear[:4]) - 3')) + '-' + '12-31'
return [firstYear,secondYear,thirdYear,fourthYear]
elif option == '最近一年':
if '12-31' not in oneDay:
firstYear = str(eval('int(oneDay[:4]) - 1')) + '-' + '12-31'
secondYear = str(eval('int(oneDay[:4]) - 2')) + '-' + '12-31'
else:
firstYear = oneDay
secondYear = str(eval('int(firstYear[:4]) - 1')) + '-' + [x for x in reportDateList if firstYear[5:] > x][0]
return [firstYear,secondYear]
elif option == '最近一期':
if oneDay[5:] not in reportDateList:
if oneDay[5:] < reportDateList[0]: #'01-02' < '03-31'
firstYear = str(eval('int(oneDay[:4]) - 1'))
firstPeriod = firstYear + '-' + reportDateList[-1]
secondPeriod = firstYear + '-' + reportDateList[-2]
elif oneDay[5:] < reportDateList[1]: #'04-05' < '06-30'
firstYear = oneDay[:4]
firstPeriod = firstYear + '-' + reportDateList[0]
secondPeriod = str(eval('int(firstYear) - 1'))+ '-' + reportDateList[3]
else:
firstYear = oneDay[:4]
#print('test: ',[x for x in reportDateList if oneDay[5:] > x])
firstPeriod = firstYear + '-' + [x for x in reportDateList if oneDay[5:] > x][-1]
secondPeriod = firstYear + '-' + [x for x in reportDateList if oneDay[5:] > x][-2]
else: #oneDay = '2015-03-31' or '2016-12-31'
if oneDay[5:] == '03-31':
firstPeriod = oneDay
secondPeriod = str(eval('int(oneDay[:4]) - 1')) + '-' + '12-31'
else:
firstPeriod = oneDay
secondPeriod = oneDay[:4] + '-' + [x for x in reportDateList if oneDay[5:] > x][-1]
return [firstPeriod,secondPeriod]
else:
raise ValueError('输入要是最近一期,最近一年,最近三年,请检查Json的输入')
oneDay = '2015-12-31'
option = '最近一年'
print(findRightDate(oneDay,option))
KYBT: KuaiYuBackTesting
交易时候用真实价格交易,算涨幅可以
0直接每日的涨跌幅相加,很方便,这是粗略的,可以看看有多粗略,
1用昨日的收盘价和涨跌幅算出今日的收盘价(假设今日除权了)
2算相应时间段的后复权涨幅
3买入的价格是买入的总市值除以买入时候的股本,用当前总市值除以买入时候的股本,就得到今日的价格,当然这个价格和实际的而价格肯定不一样,但是要算涨幅的话是绰绰有余了。
need to know the index pool that makes up the index
1\ 测试工具 任务调度 模拟很多人跑 3月25日之前(周日)弄好,4月之前确保益盟好了,我们立马就好
1\account PrintMe
2\cash < 0, @property
3\calculate zhangfu
1\ the redis server\clientAPI server\Robot should be on at the very first
4 处理停牌无法卖出,涨停无法买入,跌停无法卖出
5 benchmark
6 持仓分析
7 Method1 has issue
8 开盘前跌停无法卖出,但是还没有考虑若之后打开跌停,可以卖出的情况。 涨停买入也一样
9 假设所有的涨幅都按照( 今收-昨收/昨收) 来算 updatePositionValueMethod2
10 @Property 把能用的地儿都用到
11 周线、年线运行、
12 没有考虑手续费
13 一个股票要卖 就全卖
14 用进程有用,线程没用
15 财报用publish date 不用{'Jan':'01-01','Apr':'04-01','Jul':'07-01','Oct':'10-01'} 而是用和这些基准最接近的那个publish date
16 每一个表分成一个类
17
CY_FinacialRatio_RP 我武生物 为例 上市是2014年1月21日(1月21日开始有K线)
18 添加交易记录
19 获取交易日期的得用YM数据库的
20 一次性读取所有的数据,放在内存里面,把内存当数据库,以后机器人读取就只需要读取内存的数据了,这样就飞快
21 merge API into oneAPI
22 finish objecting SQL, then create a strategy
23 整个运作流程写好,API写好,数据库格式说清楚,和益盟对接
24 当天卖掉就立即买入,净值不应该有尖头波动,调仓动作(先卖再买)是在同一时刻完成的,
25 改变程序的运行方式,不要每次读数据;而是一次性根据用户Json读取数据,
26 股票分红的当天,股价会相应的降低的;当日实际开盘价 + 分红/股数 = 当日不分红开盘价
27 learn APIstar mother fuck!
28 account 把marketValueAPI 删除掉
29 选股要针对日期来选,加上此功能
30 补充所有的指标 dataTable里面
31 在engine2 DateList 最近三年
32 261 bulk['EM_QUOTE_INDEX_DAILY'] = self.getIndexData('HS300',startDate,endDate)
33 20130704 君正集团没有bar,数据缺失 在account简单处理了 x.positionValue = 0
34 校验Json 在clientApi那里
35 需要检查之前是否跑过此策略
36 根据Json来确定唯一的编码
37 裸API 断线重连
则应该把上市之日前的财报数据都删掉 以pubDate为准
endDate pubDate
2017-12-31 00:00:00.000 2018-03-15 00:00:00.000
2017-09-30 00:00:00.000 2017-10-26 00:00:00.000
2017-06-30 00:00:00.000 2017-08-08 00:00:00.000
2017-03-31 00:00:00.000 2017-04-26 00:00:00.000
2016-12-31 00:00:00.000 2018-03-15 00:00:00.000 有问题
2016-12-31 00:00:00.000 2017-02-09 12:00:00.000
2016-09-30 00:00:00.000 2017-10-26 00:00:00.000
2016-06-30 00:00:00.000 2017-08-08 00:00:00.000
2016-03-31 00:00:00.000 2017-04-26 00:00:00.000
2015-12-31 00:00:00.000 2017-04-26 00:00:00.000 有问题
2015-12-31 00:00:00.000 2016-02-26 12:00:00.000
2015-09-30 00:00:00.000 2016-10-28 00:00:00.000
2015-06-30 00:00:00.000 2016-08-26 00:00:00.000
2015-03-31 00:00:00.000 2016-04-22 00:00:00.000
2014-12-31 00:00:00.000 2016-04-08 00:00:00.000 有问题
2014-09-30 00:00:00.000 2015-10-30 00:00:00.000
2014-06-30 00:00:00.000 2015-08-12 00:00:00.000
2014-03-31 00:00:00.000 2015-04-23 00:00:00.000
2013-12-31 00:00:00.000 2014-04-18 00:00:00.000
2013-12-31 00:00:00.000 2014-01-20 00:00:00.000
2013-06-30 00:00:00.000 2013-12-31 00:00:00.000
2012-12-31 00:00:00.000 2013-12-31 00:00:00.000
2011-12-31 00:00:00.000 2013-12-31 00:00:00.000
2010-12-31 00:00:00.000 2013-12-31 00:00:00.000
2009-12-31 00:00:00.000 2012-05-04 00:00:00.000
这些有问题的数据怎么办?艹 没法用,得用原始三表中的公布日期,那么暂时就用标准日期来运行吧、下一个版本再完善。
ISSUE
因为获取的成分股都是最新一期的,那么用这些成分股去取历史中的股票,就可能不存在。
比如上证50的最新的成分股有江苏银行,但是历史上取不到这只股票,因为那时候还没有发行。
所以取50只股票,有可能取不满50只。 比如某一天之前没有江苏银行,交易的时候没有,某一天起,突然有了,
那也不能凭空把江苏银行加入到持仓中,所以held需要持仓中的票来held,
请求的字段:
请求的字段:
{
"strategy_id": 45534, //必须
"start":0, //必须,表示请求从第几个周期开始的数据
"end":0 //可选,表示请求区间的结束下标
}
返回的字段:
{
"strategy_id":'544234',
"status":0, //0表示数据未生成,1表示数据生成中,2表示数据已经生成完毕
"length":10332, //本策略的总周期数,用于画出x轴长度
"start": "0", //数据开始标记
"end": "50", //数据结束标记
"data":[ //根据请求的起始和结束下表,返回区间的数据
{
"x":"1999-01-03",
"y":{
"pot..":0.4,
"cash":0.3,
...
}
},
{
"x":"1999-01-04",
"y":{
"pot..":0.4,
"cash":0.3,
...
},
},
...
]
}
\ No newline at end of file
https://guorn.com/stock?category=stock
https://guorn.com/stock?category=stock
https://www.fundsmart.com.cn/stock/screener
# encoding: utf-8
from threading import Thread
import time
import datetime
import redis
import sys
'''
from .server.redisConfig import redisConf,jsonList
from .BT import engine2
from .util.kySDK import *
from .BT.evaluator import evaluator
'''
import os
#sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
#print(sys.path)
from .server.redisConfig import redisConf,jsonList
from .BT.engine2 import Engine
from .util.kySDK import *
from .BT.evaluator import evaluator
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.CRITICAL)
import multiprocessing as mp
class BTRobot():
def __init__(self):
self.r = redis.StrictRedis(host=redisConf['host'], port=redisConf['port'], db=0)
self.robotNum = 0
def cleanQueue(self):
print('先清空队列,队列长度为: %d'%self.r.llen(jsonList))
while self.r.llen(jsonList) != 0:
self.r.rpop(jsonList)
def process_task(self):
# 从队列中取参数
while True:
try:
params = self.r.brpop(jsonList)
params = eval(params[1].decode())
print('params',type(params),params)
myBT = Engine(params) #initialize engine
if myBT.stockPicker['pick'] == False:
myBT.backTesting()
evaluator(myBT.strategyID,myBT.BTRecords,myBT.IndexRecords,plot = False)
elif myBT.stockPicker['pick'] == True:
for day in myBT.stockPicker['dateList']:
myBT.stockPicking(day) #是选的哪一天的???
except Exception as e:
print(e)
def turnOn(self,processNum):
self.cleanQueue()
print('开启 %d 个机器人'%processNum)
processes = [mp.Process(target=self.process_task()) for x in range(processNum)]
# Run processes
for p in processes:
p.start()
# Exit the completed processes
for p in processes:
p.join()
if __name__ == '__main__':
myRobot = BTRobot()
myRobot.turnOn(20)
# encoding: utf-8
from apistar import Route, App
import redis
from .redisConfig import redisConf,jsonList
from apistar import http
import pyodbc
r = redis.StrictRedis(host=redisConf['host'], port=redisConf['port'])
import json
"""
API 设计
========================================================
"""
# API 接收用户选股参数 post
class clientAPI():
DBcnxn = pyodbc.connect(DSN = 'TEST')
DBcursor = DBcnxn.cursor()
def __init__(self):
pass
class CORSMiddleware(App):
"""Add 跨域访问 Cross-origin resource sharing headers to every request."""
def __init__(self, origin='*', **kwargs):
super().__init__(**kwargs)
self.origin = origin
def __call__(self, environ, start_response):
def add_cors_headers(status, headers, exc_info=None):
headers = http.Headers(headers)
headers.add("Access-Control-Allow-Origin", self.origin) # 跨域访问
headers.add("Access-Control-Allow-Headers", "Origin, Content-Type, Authorization")
headers.add("Access-Control-Allow-Credentials", "true")
headers.add("Access-Control-Allow-Methods", "GET , POST, OPTIONS") # 跨域访问
return start_response(status, headers.to_list())
if environ.get("REQUEST_METHOD") == "OPTIONS": # 跨域访问
#add_cors_headers("200 OK", [("Access-Control-Allow-Origin", "*"),("Access-Control-Allow-Methods","GET, PUT, POST, DELETE")])
add_cors_headers("200 OK", [("Best-NBA-player","LeBron James")])
return [b'200 OK']
return super().__call__(environ, add_cors_headers)
def checkDict(self,mydict):
# 需要检查策略ID的唯一性,和数据库中的所有策略对照
print('\ncheckDict: ',type(mydict),mydict)
findStrategyIDCommand = 'select DISTINCT(strategyID) from BT.[dbo].BTrecords'
self.DBcursor.execute(findStrategyIDCommand)
raw1 = self.DBcursor.fetchall()
#print('checkDict',type([x[0] for x in raw1]),[x[0] for x in raw1])
if mydict['strategyID'] in [x[0] for x in raw1]:
print('已经跑过的策略')
def strategyParams(self,request: http.Request)-> http.JSONResponse: #post
"""接收策略参数"""
print('-----------------------------------接收策略参数------------------------------------------------')
print('\n...',type(request.body.decode('utf-8')),request.body.decode('utf-8'))
print('end')
mydict = json.loads(request.body.decode('utf-8'))
#print('type of mydict',type(mydict))
with open('./postedID.txt','a+') as f:
f.write(mydict['strategyID'])
f.write('\n')
if isinstance(mydict,str):
mydict = json.loads(mydict)
#checkDict(mydict)
r.lpush(jsonList, str(mydict)) # myJson is the type of str
#headers = {'Access-Control-Allow-origin':'*','Access-Control-Allow-Methods': 'GET, POST, OPTIONS'} # 跨域访问
return http.JSONResponse(mydict,status_code=200, headers={})
#return str(mydict)
# API2 获取策略的运行结果 get
def strategyResults(self,strategyID,start,end = 0):
'''
end 可选
"""返回策略运行结果"""
0 strategyID VARCHAR(255),
1 status VARCHAR(255)),
2 startDate VARCHAR(255),
3 endDate VARCHAR(255),
4 length VARCHAR(255),
5 btdate VARCHAR(255),
6 positionList VARCHAR(255),
7 cash VARCHAR(255),
8 positionListValue VARCHAR(255),
9 totalValue VARCHAR(255),
10 tradingDayAPI VARCHAR(8000);
请求的字段:
{
"strategy_id": 45534, //必须
"start":0, //必须,表示请求从第几个周期开始的数据
"end":0 //可选,表示请求区间的结束下标
}
'''
qur = "select top 1 * from BT.[dbo].BTrecords where strategyID = '%s' "%(strategyID)
#print(qur)
self.DBcursor.execute(qur)
raw1 = self.DBcursor.fetchall()
if len(eval(str(raw1))) == 0:
return http.JSONResponse(json.dumps(None),status_code=200, headers={})
for l in eval(str(raw1)):
length = l[4]
startDate = l[2]
endDate = l[3]
status = l[1]
tradingDates = eval(l[10])
start = int(start)
end = int(end)
length = int(length)
if start <= 0:
pickStartDate = startDate
elif start < length:
pickStartDate = tradingDates[start]
else:
pickStartDate = endDate
if end <= 0:
pickEndDate = endDate
elif end < length:
pickEndDate = tradingDates[end]
else:
pickEndDate = endDate
#items = 'btdate,positionList,cash,positionListValue,totalValue,strategyID'
query = "select * from BT.[dbo].BTrecords where strategyID = '%s' and btdate >= '%s' and btdate <= '%s' order by btdate"%(
strategyID,pickStartDate,pickEndDate)
#print(query)
self.DBcursor.execute(query)
#DBcursor.execute('select * from BTrecords')
raw = self.DBcursor.fetchall()
dataList = list()
for ll in eval(str(raw)):
tempDict = dict()
tempDict['btdate'] = ll[5]
tempDict['positionList'] = ll[6]
tempDict['cash'] = ll[7]
tempDict['positionListValue'] = ll[8]
tempDict['totalValue'] = ll[9]
dataList.append(tempDict)
end = start + len(dataList) # tell user how much data will be sent
mydict = dict()
mydict['strategy_id'] = list()
mydict['status'] = status
mydict['length'] = length
mydict['start'] = start
mydict['end'] = end
mydict['data'] = dataList
#headers = {'Access-Control-Allow-origin':'*','Access-Control-Allow-Methods': 'GET, POST, OPTIONS'} # 跨域访问
return http.JSONResponse(mydict,status_code=200, headers={})
#return json.dumps(mydict,ensure_ascii=False)
def serve(self):
routes = [
Route('/p', "POST", self.strategyParams),
Route('/g', "GET", self.strategyResults),
]
#app = App(routes=routes)
#app = App(routes=routes, event_hooks=event_hooks)+
app = self.CORSMiddleware(
origin='*',
routes=routes,
)
app.serve('0.0.0.0',8080,use_debugger=True,use_reloader=True)
if __name__ == "__main__":
myserver = clientAPI()
myserver.serve()
# encoding: utf-8
from apistar import Route, App
import redis
from redisConfig import redisConf,jsonList
from apistar import http
import pyodbc
r = redis.StrictRedis(host=redisConf['host'], port=redisConf['port'])
import json
#from apistar.frameworks.wsgi import WSGIApp as App
#from wsgicors import CORS
#class CORSApp(App):
# def __call__(self, environ, start_response):
# cors = CORS(super().__call__, headers='*', methods='*', maxage='180', origin='*')
# return cors(environ, start_response)
"""
API 设计
========================================================
"""
global DBcnxn,DBcursor
DBcnxn = pyodbc.connect(DSN = 'TEST')
DBcursor = DBcnxn.cursor()
# API 接收用户选股参数 post
class CORSMiddleware(App):
"""Add Cross-origin resource sharing headers to every request."""
def __init__(self, origin='*', **kwargs):
super().__init__(**kwargs)
self.origin = origin
def __call__(self, environ, start_response):
def add_cors_headers(status, headers, exc_info=None):
headers = http.Headers(headers)
headers.add("Access-Control-Allow-Origin", self.origin)
headers.add("Access-Control-Allow-Headers", "Origin, Content-Type, Authorization")
headers.add("Access-Control-Allow-Credentials", "true")
headers.add("Access-Control-Allow-Methods", "GET , POST, OPTIONS")
return start_response(status, headers.to_list())
if environ.get("REQUEST_METHOD") == "OPTIONS":
#add_cors_headers("200 OK", [("Access-Control-Allow-Origin", "*"),("Access-Control-Allow-Methods","GET, PUT, POST, DELETE")])
add_cors_headers("200 OK", [("Best-NBA-player","James")])
return [b'200 OK']
return super().__call__(environ, add_cors_headers)
class CustomHeadersHook():
def on_response(self, response: http.Response):
response.headers['Access-Control-Allow-origin'] = '*'
response.headers['Access-Control-Allow-Method'] = 'POST,GET,OPTIONS'
return response
#def on_request(self, request: http.Request):
# request.headers['Access-Control-Allow-origin'] = '*'
event_hooks = [CustomHeadersHook()]
def checkDict(mydict):
# 需要检查策略ID的唯一性,和数据库中的所有策略对照
print('\ncheckDict: ',type(mydict),mydict)
findStrategyIDCommand = 'select DISTINCT(strategyID) from BT.[dbo].BTrecords'
DBcursor.execute(findStrategyIDCommand)
raw1 = DBcursor.fetchall()
#print('checkDict',type([x[0] for x in raw1]),[x[0] for x in raw1])
if mydict['strategyID'] in [x[0] for x in raw1]:
print('已经跑过的策略')
def strategy_params(request: http.Request)-> http.JSONResponse: #post
"""接收策略参数"""
print('-----------------------------------接收策略参数------------------------------------------------')
print('\n...',type(request.body.decode('utf-8')),request.body.decode('utf-8'))
print('end')
mydict = json.loads(request.body.decode('utf-8'))
#print('type of mydict',type(mydict))
with open('./postedID.txt','a+') as f:
f.write(mydict['strategyID'])
f.write('\n')
if isinstance(mydict,str):
mydict = json.loads(mydict)
#checkDict(mydict)
r.lpush(jsonList, str(mydict)) # myJson is the type of str
#headers = {'Access-Control-Allow-origin':'*','Access-Control-Allow-Methods': 'GET, POST, OPTIONS'} # 跨域访问
return http.JSONResponse(mydict,status_code=200, headers={})
#return str(mydict)
# API2 获取策略的运行结果 get
def strategy_results(strategyID,start,end = 0):
'''
end 可选
"""返回策略运行结果"""
0 strategyID VARCHAR(255),
1 status VARCHAR(255)),
2 startDate VARCHAR(255),
3 endDate VARCHAR(255),
4 length VARCHAR(255),
5 btdate VARCHAR(255),
6 positionList VARCHAR(255),
7 cash VARCHAR(255),
8 positionListValue VARCHAR(255),
9 totalValue VARCHAR(255),
10 tradingDayAPI VARCHAR(8000);
请求的字段:
{
"strategy_id": 45534, //必须
"start":0, //必须,表示请求从第几个周期开始的数据
"end":0 //可选,表示请求区间的结束下标
}
'''
qur = "select top 1 * from BT.[dbo].BTrecords where strategyID = '%s' "%(strategyID)
#print(qur)
DBcursor.execute(qur)
raw1 = DBcursor.fetchall()
if len(eval(str(raw1))) == 0:
return http.JSONResponse(json.dumps(None),status_code=200, headers={})
for l in eval(str(raw1)):
length = l[4]
startDate = l[2]
endDate = l[3]
status = l[1]
tradingDates = eval(l[10])
start = int(start)
end = int(end)
length = int(length)
if start <= 0:
pickStartDate = startDate
elif start < length:
pickStartDate = tradingDates[start]
else:
pickStartDate = endDate
if end <= 0:
pickEndDate = endDate
elif end < length:
pickEndDate = tradingDates[end]
else:
pickEndDate = endDate
#items = 'btdate,positionList,cash,positionListValue,totalValue,strategyID'
query = "select * from BT.[dbo].BTrecords where strategyID = '%s' and btdate >= '%s' and btdate <= '%s' order by btdate"%(
strategyID,pickStartDate,pickEndDate)
#print(query)
DBcursor.execute(query)
#DBcursor.execute('select * from BTrecords')
raw = DBcursor.fetchall()
dataList = list()
for ll in eval(str(raw)):
tempDict = dict()
tempDict['btdate'] = ll[5]
tempDict['positionList'] = ll[6]
tempDict['cash'] = ll[7]
tempDict['positionListValue'] = ll[8]
tempDict['totalValue'] = ll[9]
dataList.append(tempDict)
end = start + len(dataList) # tell user how much data will be sent
mydict = dict()
mydict['strategy_id'] = list()
mydict['status'] = status
mydict['length'] = length
mydict['start'] = start
mydict['end'] = end
mydict['data'] = dataList
headers = {'Access-Control-Allow-origin':'*','Access-Control-Allow-Methods': 'GET, POST, OPTIONS'} # 跨域访问
return http.JSONResponse(mydict,status_code=200, headers={})
#return json.dumps(mydict,ensure_ascii=False)
routes = [
Route('/p', "POST", strategy_params),
Route('/g', "GET", strategy_results),
]
#app = App(routes=routes)
#app = App(routes=routes, event_hooks=event_hooks)+
app = CORSMiddleware(
origin='*',
routes=routes,
)
if __name__ == "__main__":
app.serve('0.0.0.0',8080,use_debugger=True,use_reloader=True)
# 可视化回测子任务 - 并发队列、交互API、DB
## 开发环境准备
1. 安装redis数据库
windows参考:https://github.com/MicrosoftArchive/redis
2. python 3.6.2 + pip install -r requirements.txt
in console
python api.py run
\ No newline at end of file
# encoding: utf-8
redisConf = {
"host": "localhost",
"port": "6379"
}
jsonList = 'JsonList' # 队列参数列表
# encoding: utf-8
from apistar import Route, App
import redis
from redisConfig import redisConf,jsonList
from apistar import http
import pyodbc
import json
r = redis.StrictRedis(host=redisConf['host'], port=redisConf['port'])
"""
API 设计
========================================================
"""
# API 接收用户选股参数 post
def checkDict(mydict):
mydict = (mydict)
for key in mydict:
print('mmm',mydict[key]['stockPicker']['pick'],type(mydict[key]['stockPicker']['pick']))
#if mydict[key]['stockPicker']['pick'] == 'true'
#if mydict[key]['criteriaDcit']
return
def strategy_params(request: http.Request)-> http.JSONResponse:
"""接收策略参数"""
#print('-----------------------------------clientAPI------------------------------------------------')
mydict = json.loads(str(request.body.decode('utf-8')))
checkDict(mydict)
r.lpush(jsonList, str(mydict)) # myJson is the type of str
return str(mydict)
# API2 获取策略的运行结果 get
def strategy_results(strategyID):
"""返回策略运行结果"""
DBcnxn = pyodbc.connect(DSN = 'TEST',autocommit=True)
DBcursor = DBcnxn.cursor()
items = 'oneDay,positionList,cash,positionListValue,totalValue,strategyID'
DBcursor.execute('select %s from BTrecords items where strategyID = %s'%(items,strategyID))
raw = DBcursor.fetchall()
print(raw)
return raw
routes = [
Route('/p', "POST", strategy_params),
Route('/g', "GET", strategy_results),
]
app = App(routes=routes)
if __name__ == "__main__":
app.serve('0.0.0.0',8080,use_debugger=False)
import ky
sdk = ky.Api("http://data-api.kuaiyutech.com/api.rpc")
import datetime
def getIndexPoolFromKy(index):
return sdk.get_symbols_by_index(index)
if __name__ == '__main__':
pass
\ No newline at end of file
if not exists(select * from sys.databases where name = 'BT')
CREATE database BT;
IF EXISTS (SELECT * FROM BT.INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'BTrecords')
create table BT.BTrecords(
btdate TEXT,
positionList VARCHAR(255),
cash VARCHAR(255),
positionListValue VARCHAR(255),
totalValue VARCHAR(255),
strategyID VARCHAR(255) primary key
);
Insert into BT.BTrecords (btdate,positionList,positionListValue,totalValue,strategyID) values ('2015-05-05',1,1,1,'3')
# 这是sql创建DB,table,删除,insert的学习
import pyodbc
DBcnxn = pyodbc.connect(DSN = 'TEST',autocommit=True)
DBcursor = DBcnxn.cursor()
createDBcommand = \
'''
if not exists(select * from sys.databases where name = N'BT')
BEGIN
CREATE database BT;
END
'''
createTableCommand = """
IF NOT EXISTS (SELECT * FROM BT.INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'BTrecords')
begin
create table BT.[dbo].BTrecords(
btdate VARCHAR(255),
positionList VARCHAR(255),
cash VARCHAR(255),
positionListValue VARCHAR(255),
totalValue VARCHAR(255),
strategyID VARCHAR(255));
end
"""
#DBcursor.execute(createCommand)
dropTableCommand = """
IF EXISTS (SELECT * FROM BT.INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'BTrecords')
begin
drop table BT.[dbo].BTrecords;
end
"""
#DBcursor.execute(dropCommand)
DBcursor.execute(createDBcommand + createTableCommand )
#print(DBcursor.fetchall())
#insertCommand = "Insert into BT.[dbo].BTrecords (btdate,positionList,cash,positionListValue,totalValue,strategyID) values (%s,%0.2f,%0.2f,%0.2f,%0.2f,%s);"%\
('2015-05-05',2,1222,32,12312,'1000')
insertCommand = "INSERT INTO BT.dbo.BTrecords VALUES (%s,%0.0f,%0.2f,%0.2f,%0.2f,%s);"%('2015-05-05',2,1222,32,12312,'1000')
print(insertCommand)
DBcursor.execute(insertCommand)
DBcursor.execute('select * from BT.dbo.BTrecords')
raw = DBcursor.fetchall()
print(raw)
from KYBT import robot
import multiprocessing as mp
if __name__ == '__main__':
myRobot = robot.BTRobot()
myRobot.turnOn(20)
from KYBT.server import clientAPI
if __name__ == "__main__":
myserver = clientAPI.clientAPI()
myserver.serve()
\ No newline at end of file
5
5
306179
5
403436
90668
205596
191134
460572
972725
57305
63785
662441
128579
754058
640397
718725
816451
942501
5
5
5
from KYBT.API import marketValueAPI
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.CRITICAL)
myAPI = marketValueAPI.API()
stockList = myAPI.getHS300Name()
for stock in stockList:
logger.critical(stock)
myAPI.getMarketValueOneStock('2015-05-05','2016-05-05',stock)
# try thread and multiprocess on YMDB
from KYBT.API import marketValueAPI
portforlio = ['600000','000002','600485'] # 'all'|'HS300'|'SZ500'|'SZ50'|['600000','000002',...]
portforlio = 'all'
stockID = '300357'
startDate = '2014-01-10'
endDate = '2018-11-01'
sdk = marketValueAPI.API()
data1 = sdk.getMarketValueOneStock(startDate,endDate,stockID)
data2 = sdk.getMarketValueOneDay(startDate,portforlio)
from multiprocessing import Pool
import os, time, random
def getData(name):
print('Run task %s (%s)...' % (name, os.getpid()))
sdk = marketValueAPI.API()
data1 = sdk.getMarketValueOneStock(startDate,endDate,stockID)
data2 = sdk.getMarketValueOneDay(startDate,portforlio)
#print(data2)
def long_time_task(name):
print('Run task %s (%s)...' % (name, os.getpid()))
start = time.time()
time.sleep(random.random() * 3)
end = time.time()
print('Task %s runs %0.2f seconds.' % (name, (end - start)))
if __name__=='__main__':
print('Parent process %s.' % os.getpid())
p = Pool()
for i in range(5):
p.apply_async(long_time_task, args=(i,))
#p.apply_async(getData, args=(i,))
print('Waiting for all subprocesses done...')
p.close()
p.join()
print('All subprocesses done.')
# try thread and multiprocess on YMDB
from KYBT.API import marketValueAPI
portforlio = ['600000','000002','600485'] # 'all'|'HS300'|'SZ500'|'SZ50'|['600000','000002',...]
portforlio = 'all'
stockID = '300357'
startDate = '2014-01-10'
endDate = '2018-11-01'
sdk = marketValueAPI.API()
data1 = sdk.getMarketValueOneStock(startDate,endDate,stockID)
data2 = sdk.getMarketValueOneDay(startDate,portforlio)
from multiprocessing import Pool
import os, time, random
def getData(name):
print('Run task %s (%s)...' % (name, os.getpid()))
sdk = marketValueAPI.API()
data1 = sdk.getMarketValueOneStock(startDate,endDate,stockID)
data2 = sdk.getMarketValueOneDay(startDate,portforlio)
#print(data2)
def long_time_task(name):
print('Run task %s (%s)...' % (name, os.getpid()))
start = time.time()
time.sleep(random.random() * 3)
end = time.time()
print('Task %s runs %0.2f seconds.' % (name, (end - start)))
if __name__=='__main__':
import time, threading
def process_main():
threads = []
for i in range(10):
t = threading.Thread(target=getData,args=(i,))
threads.append(t)
for thread in threads:
thread.start()
for thread in threads:
thread.join()
process_main()
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.CRITICAL)
import time
import requests
import json
# "stockPicker":{'pick':True,'dateList':['2014-07-01','2014-07-05']},
# "stockPicker":{'pick':False,'dateList':None},
strategy1 = \
{
"stockPicker":{'pick':True,'dateList':None},
"criteriaDict" : {'存货周转率':['最近一年',None,5],'主营收入同比':['最近一年',None,10],'流动资产':['最近一期',None,100000000]},
"portforlio": ['600000'],
"benchmark": 'ZZ500',
"money": 5000000,
"startDate": '2016-04-01',
"endDate": '2018-04-12',
"turnover": 10,
"tradingHabit": "OpenPrice",
"status":"init"
}
inputJson1 = \
{
'1':strategy1
}
strategy2 = \
{
"stockPicker":{'pick':True,'dateList':['2015-05-05']},
"criteriaDict" : {'存货周转率':['最近一年',5,None],'主营收入同比':['最近一年',50,20],'流动资产':['最近一期',5000000,None]},
"portforlio": 'SZ50',
"benchmark": 'ZZ500',
"money": 5000000,
"startDate": '2017-04-01',
"endDate": '2018-04-12',
"turnover": 10,
"tradingHabit": "OpenPrice",
"status":"init"
}
inputJson2 = \
{
'2':strategy2
}
strategy3 = \
{
"stockPicker":{'pick':False,'dateList':None},
"criteriaDict" : {'存货周转率':['最近一年',None,None],'主营收入同比':['最近一年',None,None],'流动资产':['最近一期',None,None]},
"portforlio": 'SZ50',
"benchmark": 'SZ50',
"money": 5000000,
"startDate": '2016-04-01',
"endDate": '2018-04-12',
"turnover": 10,
"tradingHabit": "OpenPrice",
"status":"init"
}
inputJson3 = \
{
'3':strategy3
}
#jsonList = [inputJson1,inputJson1,inputJson1,inputJson1,inputJson1]
'''
jsonList = []
for i in range(50):
jsonList.append(inputJson1)
'''
jsonList = [inputJson1,inputJson2,inputJson3]
#jsonList = [inputJson1,inputJson3]
'''
for json in jsonList:
r = requests.post('http://127.0.0.1:8080/post',params = {'myJson':str(json)} ) #
print(r.status_code)
'''
for j in jsonList:
r = requests.post('http://office.caisiapp.com:8488/p',json = json.dumps(j,ensure_ascii=False))#
print(r.status_code)
:: to get the clientAPI server running
cd D:\Project\YM\BackTesting Frame\KYBT\server
::python clientAPI.py run --port 8080 --host 0.0.0.0
python clientAPI.py
:: to get the clientAPI server running
cd D:\Project\YM\BackTesting Frame\KYBT\server
python clientAPI.py run
redis-cli
config set stop-writes-on-bgsave-error no
\ No newline at end of file
redis-server
:: redis-server is running on default when the PC starts
\ No newline at end of file
:: to get the robot running to constantly check the queue
cd D:\Project\YM\BackTesting Frame\
python robot.py
start = '2017-12-20'
end = '2018-03-20'
import datetime
import ky
sdk = ky.Api("http://data-api.kuaiyutech.com/api.rpc")
print( 'trading date',sdk.get_trading_dates_by_date_range(end,start, exchange='SHSE') )
print(' ')
print( sdk.get_symbols_by_index('SHSE.000905'))
def getMostRecentTradingDayFromKy():
end = datetime.datetime.today()
start = end - datetime.timedelta(70)
end = datetime.datetime.strftime(end,'%Y-%m-%d')
start = datetime.datetime.strftime(start,'%Y-%m-%d')
return sdk.get_trading_dates_by_date_range(end,start,exchange='SHSE')[-1]
print(getMostRecentTradingDayFromKy())
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册登录 后发表评论