跳转到内容

开始写策略

简单但是完整的策略

先来看一个简单但是完整的策略:

python
def initialize(context):
    set_universe('600570.XSHG')

def handle_data(context, data):
    pass

一个完整策略只需要两步:

  1. set_universe: 设置我们要操作的股票池,上面的例子中,只操作一支股票: '600570.XSHG',恒生电子。所有的操作只能对股票池的标的进行。
  2. 实现一个函数: handle_data。

这是一个完整的策略,但是我们没有任何交易,下面我们来添加一些交易

添加一些交易

python
def initialize(context):
    g.security = '600570.XSHG'
    # 是否创建订单标识
    g.flag = False
    set_universe(g.security)

def handle_data(context, data):
    if not g.flag:
        order(g.security, 1000)
        g.flag = True

这个策略里,当我们没有创建订单时就买入 1000 股'600570.XSHG',具体的下单 API 请看order函数。这里我们有了交易,但是只是无意义的交易,没有依据当前的数据做出合理的分析。

实用的策略

下面我们来看一个真正实用的策略

在这个策略里,我们会根据历史价格做出判断:

  • 如果上一时间点价格高出五天平均价 1%,则全仓买入
  • 如果上一时间点价格低于五天平均价,则空仓卖出
python
def initialize(context):
    g.security = '600570.XSHG'
    set_universe(g.security)

def handle_data(context, data):
    security = g.security
    sid = g.security

    # 取得过去五天的历史价格
    df = get_history(5, '1d', 'close', security, fq=None, include=False)

    # 取得过去五天的平均价格
    average_price = round(df['close'][-5:].mean(), 3)

    # 取得上一时间点价格
    current_price = data[sid]['close']

    # 取得当前的现金
    cash = context.portfolio.cash

    # 如果上一时间点价格高出五天平均价1%, 则全仓买入
    if current_price > 1.01*average_price:
        # 用所有 cash 买入股票
        order_value(g.security, cash)
        log.info('buy %s' % g.security)
    # 如果上一时间点价格低于五天平均价, 则空仓卖出
    elif current_price < average_price and get_position(security).amount > 0:
        # 卖出所有股票,使这只股票的最终持有量为0
        order_target(g.security, 0)
        log.info('sell %s' % g.security)

模拟盘和实盘注意事项

关于持久化

为什么要做持久化处理

服务器异常、策略优化等诸多场景,都会使得正在进行的模拟盘和实盘策略存在中断后再重启的需求,但是一旦交易中止后,策略中存储在内存中的全局变量就清空了,因此通过持久化处理为量化交易保驾护航必不可少。

量化框架持久化处理

使用 pickle 模块保存股票池、账户信息、订单信息、全局变量 g 定义的变量等内容。

注意事项:

  1. 框架会在before_trading_start(隔日开始)handle_dataafter_trading_end事件后触发持久化信息更新及保存操作;
  2. 券商升级/环境重启后恢复交易时,框架会先执行策略initialize函数再执行持久化信息恢复操作。如果持久化信息保存有策略定义的全局对象 g 中的变量,将会以持久化信息中的变量覆盖掉initialize函数中初始化的该变量。
  3. 全局变量 g 中不能被序列化的变量将不会被保存。您可在initialize中初始化该变量时名字以'__'开头;
  4. 涉及到 IO(打开的文件,实例化的类对象等)的对象是不能被序列化的;
  5. 全局变量 g 中以'__'开头的变量为私有变量,持久化时将不会被保存;

示例

python
class Test(object):
    count = 5

    def print_info(self):
        self.count += 1
        log.info("a" * self.count)


def initialize(context):
    g.security = "600570.XSHG"
    set_universe(g.security)
    # 初始化无法被序列化类对象,并赋值为私有变量,落地持久化信息时跳过保存该变量
    g.__test_class = Test()

def handle_data(context, data):
    # 调用私有变量中定义的方法
    g.__test_class.print_info()

策略中持久化处理方法

使用 pickle 模块保存 g 对象(全局变量)。

示例

python
import pickle
from collections import defaultdict
'''
持仓N日后卖出,仓龄变量每日pickle进行保存,重启策略后可以保证逻辑连贯
'''
def initialize(context):
    #获取研究路径
    g.notebook_path = get_research_path()    
    #尝试启动pickle文件
    try:
        with open(g.notebook_path+'hold_days.pkl','rb') as f:
            g.hold_days = pickle.load(f)
    #定义空的全局字典变量
    except:
        g.hold_days = defaultdict(list)
    g.security = '600570.XSHG'
    set_universe(g.security)

# 仓龄增加一天
def before_trading_start(context, data):
    if g.hold_days:
        g.hold_days[g.security] += 1

# 每天将存储仓龄的字典对象进行pickle保存
def handle_data(context, data):
    if g.security not in list(context.portfolio.positions.keys()) and g.security not in g.hold_days:
        order(g.security, 100)
        g.hold_days[g.security] = 1
    if g.hold_days:
        if g.hold_days[g.security] > 5:
            order(g.security, -100)
            del g.hold_days[g.security]
    with open(g.notebook_path+'hold_days.pkl','wb') as f:
        pickle.dump(g.hold_days,f,-1)

策略中支持的代码尾缀

市场品种尾缀全称尾缀简称
上海市场证券XSHGSS
深圳市场证券XSHESZ
北京市场证券NEEQ
指数XBHS
中金所期货CCFX
沪港通XHKG-SS
深港通XHKG-SZ

关于异常处理

为什么要做异常处理

交易场景数据缺失等原因会导致策略运行过程中常规的处理出现语法错误,导致策略终止,所以需要做一些异常处理的保护。以下是一些基本的处理方法介绍。

示例

python
try:
    # 尝试执行的代码
    print(a)
except:
    # 如果在try块执行异常
    # 则执行except块代码
    a = 1
    print(a)
python
try:
    # 尝试执行的代码
    print(a)
except Exception as e:
    # 使用as关键字可以获取异常的实例
    print("出现异常,error为: %s" % e)
    a = 1
    print(a)
python
try:
    a = 1
    print(a)
except:
    print(a)
else:
    # 如果try块成功执行,没有引发异常,可以选择性地添加一个else块。
    print('执行正常')
python
try:
    a = 1
    print(a)
except:
    print(a)
finally:
    # 无论是否发生异常,finally块中的代码都将被执行。这可以用来执行一些清理工作,比如关闭文件或释放资源。
    print('执行完毕')

关于限价交易的价格

可转债、ETF、LOF的价格是小数点三位。

股票的价格是小数点两位。

股指期货的价格是小数点一位。

港股通的价格是小数点三位。

用户在使用限价单委托(如order()入参limit_price)和市价委托保护限价(order_market()入参limit_price)的场景时务必要对入参价格的小数点位数进行处理,否则会导致委托失败。