最近策略频繁回撤,跑不赢标普500指数,所以对策略简单修改,以待后效。
新加入的代码
def get_if_trade_day():
infile = open('countday.dat','r')
incontent = infile.read()
infile.close()
num=int(incontent)
if num !=0 :
num = (num+1)%4
outfile = open('countday.dat','w')
outfile.write(str(num))
outfile.close()
return False
else :
num = (num+1)%4
outfile = open('countday.dat','w')
outfile.write(str(num))
outfile.close()
return True
在策略文件目录下建立countday.dat文件写入0,在crontab配置里改成每周一晚上9点定时运行,get_if_trade_day()函数会每四次返回一次True,大概四周调仓一次
import os
import logging
import traceback
import pandas as pd
import numpy as np
import talib as ta
import re
import math
import threading
import time
import sys
import signal
import copy
from scipy import stats # using this for the reg slope
from datetime import datetime
#账户设置包导入
from tigeropen.examples.client_config import get_client_config
#交易包导入
from tigeropen.trade.domain.order import ORDER_STATUS
from tigeropen.trade.request.model import AccountsParams
from tigeropen.common.response import TigerResponse
from tigeropen.tiger_open_client import TigerOpenClient
from tigeropen.trade.trade_client import TradeClient
from tigeropen.quote.request import OpenApiRequest
from tigeropen.common.util.contract_utils import option_contract, future_contract
#行情包导入
from tigeropen.examples.sp500 import save_sp500_tickers as getsp500code
from tigeropen.common.consts import Market, QuoteRight, BarPeriod,Language
from tigeropen.quote.quote_client import QuoteClient
momentum_window = 60 # first momentum window.
momentum_window2 = 90 # second momentum window
minimum_momentum = 60
exclude_days = 5 # excludes most recent days from momentum calculation
#判断可以买入的股票代码
buy_set=set()
final_buy_list=[]
#当前持仓股票代码和数量的字典
cur_quant={}
goal=3
num_of_stocks=3
inv_vola_table=[]
#日志设置
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(levelname)s %(message)s',
filemode='a', )
logger = logging.getLogger('TigerOpenApi')
#客户端设置
client_config = get_client_config()
#定义交易客户端
openapi_client = TradeClient(client_config, logger=logger)
#定义行情客户端
quote_client = QuoteClient(client_config, logger=logger)
#获取标普500的20日bars
def get_price(bars_num):
stocks = read_sp_500code()
df=pd.DataFrame(columns=['symbol','time','open','high','low','close','volume'])
#每次取数据要取的条目数量
items=math.floor(1000/bars_num)
stock_num=len(stocks)
print(stock_num)
roundtrip=math.floor(stock_num/items)
print(roundtrip)
for i in range(roundtrip):
bars = quote_client.get_bars(stocks[i*items:i*items+items],period=BarPeriod.DAY,begin_time=-1, end_time=-1,right=QuoteRight.BR,limit=bars_num)
df=df.append(bars,ignore_index=True)
print(bars)
bars = quote_client.get_bars(stocks[roundtrip*items:stock_num],\
period=BarPeriod.DAY,begin_time=-1,end_time=-1,right=QuoteRight.BR,limit=bars_num)
df=df.append(bars,ignore_index=True)
df.drop(['time','open','high','low','volume'],axis=1,inplace=True)
print(df)
return_df=pd.DataFrame(columns=stocks)
for i in range(len(stocks)):
close = df[i*bars_num:i*bars_num+bars_num]['close'].values
print(close)
print(type(close))
return_df[stocks[i]]=close
print(return_df)
return return_df
def inv_vola_calc(ts):
"""
Input: Price time series.
Output: Inverse exponential moving average standard deviation.
Purpose: Provides inverse vola for use in vola parity position sizing.
"""
returns = np.log(ts).diff()
stddev = returns.ewm(halflife=20, ignore_na=True, min_periods=0,
adjust=True).std(bias=False).dropna()
return 1 / stddev.iloc[-1]
def slope(ts):
"""
Input: Price time series.
Output: Annualized exponential regression slope, multipl
"""
x = np.arange(len(ts))
log_ts = np.log(ts)
#print('This is ts',ts)
#print('This is log_ts:\n',log_ts)
#print(' ')
slope, intercept, r_value, p_value, std_err = stats.linregress(x, log_ts)
#print(slope)
annualized_slope = (np.power(np.exp(slope), 250) - 1) * 100
#print('This is annualized_slope',annualized_slope)
return annualized_slope * (r_value ** 2)
def generate_trade_info():
stocks = read_sp_500code()
hist_window = max(momentum_window,
momentum_window2) + exclude_days
hist=get_price(hist_window)
print(hist)
data_end = -1 * exclude_days # exclude most recent data
momentum1_start = -1 * (momentum_window + exclude_days)
momentum_hist1 = hist[momentum1_start:data_end]
momentum2_start = -1 * (momentum_window2 + exclude_days)
momentum_hist2 = hist[momentum2_start:data_end]
momentum_list = momentum_hist1.apply(slope) # Mom Window 1
momentum_list2 = momentum_hist2.apply(slope) # Mom Window 2
# Combine the lists and make average
momentum_concat = pd.concat((momentum_list, momentum_list2))
mom_by_row = momentum_concat.groupby(momentum_concat.index)
mom_means = mom_by_row.mean()
# Sort the momentum list, and we've got ourselves a ranking table.
ranking_table = mom_means.sort_values(ascending=False)
buy_list = ranking_table[:num_of_stocks]
global final_buy_list
final_buy_list = buy_list[buy_list > minimum_momentum] # those who passed minimum slope requirement
# Calculate inverse volatility, for position size.
global inv_vola_table
inv_vola_table = hist[buy_list.index].apply(inv_vola_calc)
global buy_set
buy_set = set(final_buy_list)
def trade(account):
#generate_trade_info()
#account = client_config.paper_account
curset=get_curset(account)
#设计pos_local_curset来应对主观交易的问题
pos_local_curset=get_local_curset(account)
temp_pos_local_curset = copy.deepcopy(pos_local_curset)
# Sell positions no longer wanted.
for security in temp_pos_local_curset:
if (security not in set(final_buy_list.index)):
print('preparing substitution')
print(security)
contract = openapi_client.get_contracts(security)[0]
print(cur_quant)
quant=cur_quant[security]
print(quant)
pos_local_curset.remove(security)
order = openapi_client.create_order(account, contract, 'SELL', 'MKT' , cur_quant[security])
openapi_client.place_order(order)
# sum inv.vola for all selected stocks.
sum_inv_vola = np.sum(inv_vola_table)
vola_target_weights = inv_vola_table / sum_inv_vola
cur_asset=openapi_client.get_assets(account=account)
potfolio=cur_asset[0]
print(potfolio)
print(potfolio.summary)
#cash=potfolio.summary.available_funds
cash=potfolio.summary.net_liquidation*0.5
print(final_buy_list)
print(type(final_buy_list))
for security in set(final_buy_list.index):
# allow rebalancing of existing, and new buys if can_buy, i.e. passed trend filter.
weight = vola_target_weights[security]
contract = openapi_client.get_contracts(security)[0]
#print(contract)
#print(type(tobuy_list[i]))
#print(tobuy_list[i])
brief=quote_client.get_briefs(symbols=[security],right=QuoteRight.BR)
latest_price=brief[0].latest_price
quant=math.floor(cash*weight/latest_price)
opt='BUY'
if (security in curset):
net=math.floor(cash*weight/latest_price)-cur_quant[security]
if net > 0:
quant=net
if abs(net)/cur_quant[security] < 0.1 :
continue
elif net < 0:
opt='SELL'
quant=-net
if abs(net)/cur_quant[security] < 0.1 :
continue
else:
continue
else:
pos_local_curset.add(security)
if math.floor(cash*weight/latest_price) == 0 :
pos_local_curset.remove(security)
if quant == 0:
continue
order = openapi_client.create_order(account, contract, opt, 'MKT' , quant)
print('buy order sent')
openapi_client.place_order(order)
write_pos_local_curset(list(pos_local_curset),account)
#position对象:[contract: TVIX/STK/USD, quantity: 5, average_cost: 36.0764, market_price: 0.0]
def get_curset(account):
pos=openapi_client.get_positions(account=account)
print(len(pos))
curset=set()
global cur_quant
cur_quant={}
for i in range(len(pos)):
print('当前持仓\n',pos[i])
cont=pos[i].contract
code = re.search(r'[A-Z]*(?=/STK)',str(cont)).group(0)
curset.add(code)
cur_quant[code]=pos[i].quantity
return curset
def get_local_curset(account):
if client_config.account == account :
infile = open('account_local_curset.dat','r')
elif client_config.paper_account == account :
infile = open('paper_local_curset.dat','r')
incontent = infile.read()
infile.close()
if incontent.strip() == str(''):
return set()
print(incontent)
incontent=incontent.lstrip('[')
print(incontent)
incontent=incontent.rstrip(']\n')
print(incontent)
stocks = incontent.split(', ')
print(stocks)
print(len(stocks))
print(eval(stocks[0]))
new= []
for i in range(len(stocks)):
new.append(eval(stocks[i]))
return set(new)
def write_pos_local_curset(pos_local_curset,account):
if client_config.account == account :
outfile = open('account_local_curset.dat','w')
elif client_config.paper_account == account :
outfile = open('paper_local_curset.dat','w')
outfile.write(str(pos_local_curset))
outfile.close()
#卖出当前所有持仓
def all_off(account):
pos=openapi_client.get_positions(account=account)
for i in range(len(pos)):
contract=pos[i].contract
quantity=pos[i].quantity
if quantity>0:
order = openapi_client.create_order(account, contract, 'SELL', 'MKT' , quantity)
openapi_client.place_order(order)
elif quantity<0:
order = openapi_client.create_order(account, contract, 'BUY', 'MKT' , -quantity)
openapi_client.place_order(order)
if client_config.account == account :
outfile = open('account_local_curset.dat','w')
elif client_config.paper_account == account :
outfile = open('paper_local_curset.dat','w')
outfile.write('')
outfile.close()
def read_sp_500code():
infile = open('sp500code.dat','r')
incontent = infile.read()
incontent=incontent.lstrip('[')
incontent=incontent.rstrip(']')
stocks = incontent.split(', ')
new= []
for i in range(len(stocks)):
new.append(eval(stocks[i]))
return new
def before_market_open():
generate_trade_info()
def get_if_trade_day():
infile = open('countday.dat','r')
incontent = infile.read()
infile.close()
num=int(incontent)
if num !=0 :
num = (num+1)%4
outfile = open('countday.dat','w')
outfile.write(str(num))
outfile.close()
return False
else :
num = (num+1)%4
outfile = open('countday.dat','w')
outfile.write(str(num))
outfile.close()
return True
#Not Yet Open, Pre Market Trading, Market Trading
if __name__ == '__main__':
if get_if_trade_day() == False:
print('Dont trade')
sys.exit(0)
print('trade')
if 'account' == sys.argv[2] :
account = client_config.account
elif 'paper_account' == sys.argv[2] :
account = client_config.paper_account
if sys.argv[1] == 'trade':
p = threading.Thread(target=before_market_open)
p.setDaemon(True)
p.start()
time.sleep(10)
p.join()
while True:
market_status_list = quote_client.get_market_status(Market.US)
print(market_status_list)
if market_status_list[0].status == 'Trading':
trade(account)
break
time.sleep(60)
elif sys.argv[1] == 'all_off':
#get_price(95)
#trade()
#get_curset(client_config.paper_account)
all_off(account)