老虎证券美股策略——将动量策略日频调仓改成月频

最近策略频繁回撤,跑不赢标普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)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
动量选股策略是一种投资策略,它基于股票的相对强弱以及过去的表现来选择投资标的。根据动量选股策略,投资者将重点关注近期表现较好的股票,认为这些股票有较大概率在未来持续上涨。 动量选股策略的实施有多种方式。其中一种常用的方法是使用相对强弱指标,比如相对强度指标(RSI)或者相对强势指标(RS)。这些指标可以通过比较不同股票的涨跌幅来衡量它们的相对强弱,从而选择表现较好的股票。 另一种常见的方法是使用移动平均线来筛选股票。移动平均线能够平滑股票价格的波动,同时也可以反映出股票价格的长期趋势。投资者可以根据股票价格与移动平均线之间的关系来判断股票的动量,进而选取表现较好的股票。 动量选股策略的优点在于能够及时捕捉到市场的热点和涨势,并能够根据股票的表现进行动态调整。然而,动量选股策略也存在一些风险。例如,股票的过去表现并不一定能够预测其未来的涨跌,市场存在许多影响因素可能导致股票表现的变化。此外,动量选股策略需要投资者密切关注市场行情和股票的涨跌情况,对投资者的时间和精力要求较高。 总而言之,动量选股策略是一种用于选择投资标的的方法,它基于股票的相对强弱和过去的表现来进行选取。这种策略有助于投资者捕捉到市场的热点和涨势,但也需要投资者密切关注市场情况和风险的变化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值