策略设置方法

# 策略设置方法

# 期货基本信息表

序号合约品种交易所交易所编码名称价格最小单位乘数保证金率手续费计算方式手仓(平昨)续费率平今手续费率开仓手续费率
1ADCEDCE豆一1100.05按数量202
2AGSHFESHF白银1150.08按金额0.000050.000050.00005
3ALSHFESHF沪铝550.05按数量303
4AUSHFESHF黄金0.0510000.08按数量10010
5APCZCECZC苹果1100.07按数量0.500.5
6BDCEDCE豆二1100.05按数量222
7BBDCEDCE胶板0.055000.2按金额0.00010.000050.0001
8BUSHFESHF沥青2100.1按金额0.00010.00010.0001
9CDCEDCE玉米1100.05按数量1.201.2
10CFCZCECZC棉花550.05按数量4.304.3
11CSDCEDCE淀粉1100.05按数量1.501.5
12CUSHFESHF沪铜1050.09按金额0.00002500
13CYCZCECZC棉纱550.1按数量444
14FBDCEDCE纤板0.055000.2按金额0.0000500.00005
15FGCZCECZC玻璃1200.05按数量303
16FUSHFESHF燃油1500.2按金额0.000020.000020.00002
17HCSHFESHF热卷1100.1按金额0.000010.000010.00001
18IDCEDCE铁矿0.51000.05按金额0.000060.000030.00006
19ICCFFEXCFE中证5000.22000.3按金额0.0000230.0001150.000023
20IFCFFEXCFE沪深3000.23000.2按金额0.0000230.0001150.000023
21IHCFFEXCFE上证500.23000.2按金额0.0000230.0001150.000023
22JDCEDCE焦炭0.51000.05按金额0.000060.000030.00006
23JDDCEDCE鸡蛋1100.08按金额0.000150.000150.00015
24JMDCEDCE焦煤0.5600.05按金额0.000060.000030.00006
25JRCZCECZC粳稻1200.05按数量333
26LDCEDCE塑料550.05按数量202
27LRCZCECZC晚稻1200.05按数量303
28MDCEDCE豆粕1100.05按数量1.501.5
29MACZCECZC甲醇1100.07按数量1.401.4
30NISHFESHF沪镍1010.1按数量666
31OICZCECZC菜油2100.05按数量2.502.5
32PDCEDCE棕榈2100.05按数量2.502.5
33PBSHFESHF沪铅550.1按金额0.0000400.00004
34PMCZCECZC普麦1500.05按数量555
35PPDCEDCE聚丙烯150.05按金额0.000050.0000250.00005
36RBSHFESHF螺纹1100.1按金额0.00004500.000045
37RICZCECZC早稻1200.05按数量2.52.52.5
38RMCZCECZC菜粕1100.05按数量1.501.5
39RSCZCECZC菜籽1100.05按数量222
40RUSHFESHF橡胶5100.1按金额0.0000450.0000450.000045
41SFCZCECZC硅铁250.05按数量303
42SMCZCECZC锰硅250.05按数量303
43SNSHFESHF沪锡1010.09按数量303
44SRCZCECZC白糖1100.05按数量303
45TCFFEXCFE债十0.005100000.02按数量303
46TACZCECZCPTA250.05按数量333
47TFCFFEXCFE债五0.005100000.012按数量333
48VDCEDCEPVC5100000.05按数量202
49WHCZCECZC强麦1200.05按数量2.502.5
50WRSHFESHF线材1100.2按金额0.000040.000040.00004
51YDCEDCE豆油2100.05按数量2.502.5
52ZCCZCECZC郑煤0.21000.05按数量404
53ZNSHFESHF沪锌550.1按数量303
54SCINEINE原油0.110000.05按金额0.00010.00010.0001

# set_commission

定义

set_commission(per_order)

回测时, 为了模拟真实的交易场景, 系统会计算每笔交易的手续费, 在initialize方法中调用。默认状态下,是股票交易设置手续费的API,买入时手续费为成交金额的万分之3,卖出时手续费为成交金额的千分之1.3,手续费不足5元按5元收取。
此外,还可以通过PerShare和PerTrade设置手续费。
手续费设置可以动态调整,比如股指期货我们可以在不同的交易日设置不同的手续费,以便回测更能接近真实情形。

参数:

per_order – PerOrder对象 或者PerTrade对象 或者PerShare对象

示例代码


# 示例代码1
def initialize(context):
    # buy_cost,买入时手续费
    # sell_cost,卖出时手续费
    # min_cost,最少的手续费
    # 买入时手续费为成交金额的万分之3,卖出时手续费为成交金额的千分之1.3,手续费不足5元按5元收取。
    context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))


# 示例代码2
def initialize(context):
    # 股票成交时,手续费按成交次数收取,每一次成交收取5元手续费
    context.set_commission(PerTrade(5))

# 示例代码3
def initialize(context):
    # 股票成交时,手续费按成交金额一定比例收取,手续费为成交金额的千分之一
    context.set_commission(PerShare(0.001))
# 上面三个例子都是股票交易时如何设置手续费,如果是期货交易的话,可以参照如下示例代码设置
# 示例代码4
def initialize(context):
    context.set_commission(futures_commission=PerContract(cost={'IF':(0.0023, 0.0015, 0.0023)})) 

# set_slippage

定义

set_slippage(slippage)

回测时, 为了模拟真实的交易场景, 系统会设置滑点, 只能在initialize方法中调用,定义在策略中的其他位置则无效。当您下单交易时,您的订单将影响市场。您的买单驱使价格上涨,您的卖单驱使价格下滑;这通常被称为您的交易的“价格影响”。价格影响的大小取决于您的订单与当前交易量相比有多大。如果您的订单太大,那么滑点方法也会进行评估:您的交易量不能超过市场的容量,通常您也不能期望交易量超过一小部分(比如百分之5,举牌)。所有这些概念都被封装在了set_slippage方法中。当订单因为不满足滑点条件没有完全成交,未成交的部分仍然可以在第二天继续成交。这会持续直到订单完全成交或被取消。

如果您没有指定set_slippage方式,则滑点默认为VolumeShareSlippage(volume_limit = 0.025,price_impact = 0.1)(您最多可以占用单支股票交易量的2.5%)。

滑点模型是通过set_slippage完成的,目前有:FixedBasisPointsSlippage、FixedSlippage、VolumeShareSlippage,同时,也可以自定义滑点模型。

FixedBasisPointsSlippage

固定基点滑点

context.slippage.FixedBasisPointsSlippage(basis_points=5, volume_limit=0.1)

参数:

basis_point - 基点滑点
volume_limit - 成交量最大限制,默认为0.1,即10%

如果基点等于5,即0.05%,如果订单是买单,那么实际成交价是名义成交价的1(1+0.05%)倍,如果订单是卖单,实际成交价是名义成交价的1(1-0.05%)。比如名义成交价是100元,那么买单的成交价是100.05,卖单的成交价是99.95。
成交量最大限制参数确定了当根Bar最大能成交多少数量。举例,下单量是220手,volume_limit=0.1,接下来每根Bar成交量是1000手,在这种情形下,220手的订单会拆分为3笔成交(100手、100手、20手)分别在接下来三根Bar上完成撮合成交。
如果volume_limit=1.00,那么在接下来的一根Bar上最大可以成交1000手,因此220手的订单可以直接一下子成交,无须拆单。

VolumeShareSlippage

成交量比例滑点

context.slippage.VolumeShareSlippage(volume_limit=0.025, price_impact=0.1)

参数:

volume_limit - 成交量最大限制,默认为0.025
price_impact - 价格影响幅度,默认为0.1
volume_limit参数含义同上,可以参考FixedBasisPointsSlippage。比如,当前下单的订单数量是60手,接下来的几根Bar的成交量都是1000手,那么在0.025的默认值下,订单会被拆分为25、25、10依次成交。
price_impact衡量的是滑点对价格的影响幅度,默认值为0.1,滑点的计算为:price_impact(实际成交量/总成交量)^2
在前面的例子中,25手的单子的价格影响为:0.1
(25/1000)(25/1000)=0.00625%,对于10手的订单,价格影响为:0.1(10/1000)*(10/1000)=0.001%。

FixedSlippage
固定滑点

context.slippage.FixedSlippage(spresd)

参数:

spread - 价差

当采取固定滑点模型时,订单量的大小并不会影响撮合时的价格,但是蕴含假设为,下单时买一和卖一的价差-spread会影响价格。如果是买单,那么实际成交价为名义成交价+0.5spread,如果是卖单,那么实际成交价为名义成交价-0.5spread。

自定义滑点

您也可以构建一个自定义滑点模型,该模型使用您自己的逻辑将订单流转换为交易流。在initialize()函数中,您必须指定要使用的滑点模型和滑点模型将使用的任何特殊参数。您的自定义模型必须是一个继承自slippage.SlippageModel并实现process_order(self,data,order)函数的类。process_order方法必须返回一个元素(execution_price, execution_volume),它表示模型想要生成的交易价格和交易量。然后为您创建交易。具体使用请参考示例代码。

示例代码

# 示例代码1
# 自定义滑点模型
class PerStockSpreadSlippage(slippage.SlippageModel):
    # 指定初始化函数
    def __init__(self, spreads):
        # 存储spread的字典,用股票代码作为key
        self.spreads = spreads

    def process_order(self, data, my_order):
        spread = self.spreads[my_order.sid]
        price = data.current(my_order.sid, 'price')

        # 在这个模型中滑点是spread的一半
        slip_amount = spread / 2

        # 计算交易的价格影响,价格影响跟订单交易方向有关,买单会增加价格,而卖单会减少价格。
        new_price = price + (slip_amount * my_order.direction)

        return (new_price, my_order.amount)

def initialize(context):
    # 限制每单交易不超过单只股票交易额的 1%,若超过会进行分单处理,即订单不是一次性成交完毕,而是多日完成。
    context.set_slippage(VolumeShareSlippage(volume_limit=0.01))
    # 固定滑点模型,下一个买单时,价格会加0.5;而下一个卖单时,价格会减0.5。
    context.set_slippage(FixedSlippage(spread=1.00))
    context.spreads = {
        context.symbol('000001.SZA'): 0.05
    }
    # 使用自定义的滑点模型
    context.set_slippage(PerStockSpreadSlippage(context.spreads))
# 示例代码2 
# 按指定价格成交
def initialize(context):
    from zipline.finance.slippage import SlippageModel
        class FixedPriceSlippage(SlippageModel):
            def process_order(self, data, order, bar_volume=0, trigger_check_price=0):
                if order.limit is None:
                    price_field = self._price_field_buy if order.amount > 0 else self._price_field_sell
                    price = data.current(order.asset, price_field)
                else:
                    price = order.limit
                # 返回希望成交的价格和数量
                return (price, order.amount)
        context.fix_slippage = FixedPriceSlippage()
        # 设置price_field在[low,high]就能保证只要限价单价格在此范围都能成交,也符合实际情形
        context.fix_slippage = FixedPriceSlippage(price_field_buy='low', price_field_sell='high')
        context.set_slippage(us_futures=fix_slippage) # us是universe的简写,如果是股票,需要传入us_equities

def handle_data(context, data):
    # 生成限价单,订单的限价为指定的价格,如果是买单,价格高于low就能成交,并且成交价为my_price,如果是卖单,价格低于high就能成交,成交价格为my_price
    sid = context.future_symbol('RB1901.SHF')
    context.order(sid, 2, limit_price=my_price)

# set_long_only

定义

set_long_only

回测设置只允许做多,不允许做空

示例代码

def initialize(context):
    # 如果下一个订单,导致当前持股为负,则策略会抛出一个异常
    context.set_long_only()

# set_max_leverage

定义

set_max_leverage(max_leverage)

设置策略的最大杠杆限制

参数:

max_leverage – float,策略允许的最大杠杆,若没有设置则没有最大限制

示例代码

from zipline.api import set_max_leverage
def initialize(context):
    context.set_max_leverage(1)

# set_need_settle

定义

set_need_settle(need_settle)

期货策略设置是否每日结算

参数:

need_settle – bool,如果product_type为期货;默认为True,否则为False

示例代码


def initialize(context):
    context.set_need_settle(False)

# set_margin

定义

set_margin(security_code, margin)

设置期货品种的保证金比率,支持国内四大期货交易所,每个品种的默认设置请参考表《期货基本信息表》

参数:

security_code – str,期货品种
margin - float,保证金比率,数值在0-1之间

示例代码

def initialize(context):
    context.set_margin('RB', 0.1)

# set_benchmark

定义

set_benchmark(benchmark)

设置策略基准。

参数:

benchmark – Asset,这个资产将成为新的策略基准

示例代码

def initialize(context):
    context.set_benchmark(context.symbol('000903.SHA'))

# set_max_order_count

定义

set_max_order_count(max_count, on_error=’fail’)

设置在某一天可以提交的order数量上限

参数:

max_count – int,在某一天可以提交的order数量上限

示例代码

from zipline.api import set_max_order_count
def initialize(context):
    context.set_max_order_count(10)

# set_max_order_size

定义

set_max_order_size(asset=None, max_shares=None,max_notional=None,on_error=’fail’)

设置在某一天可以提交的order上限。

参数:

asset – Asset, optional,如果这个参数提供,则保证头寸限制规则只用在这个资产上
max_shares – int,optional,单次order最大股票数量上限
max_notional – float,单次order最大现金

示例代码

def initialize(context):
    context.set_max_order_size(max_notional=4000)

# set_max_position_size

定义

set_max_position_size(asset=None,max_shares=None,max_notional=None, on_error=’fail’)

针对某一头寸设置最大股票数量或最大头寸金额

参数:

asset – Asset, optional,如果这个参数提供,则保证头寸限制规则只用在这个资产上

max_shares –int,optional,单次头寸最大股票数量上限

max_notional – float,单个头寸最大金额

示例代码

def initialize(context):
    context.set_max_position_size(max_notional=4000)

# 周期执行调度函数

如果每根Bar不一定都要运行一下主函数(handle_data),因此你的策略可以回测的更快。比如按月调仓的多因子选股策略,只需要在特定的Bar上运行handle_data主函数,为此,平台提供了schedule_function这一周期执行调度函数。
不仅如此,周期执行调度函数还使用了日期和时间规则,以便更加灵活和高效。所有的周期执行调度函数都在初始化函数(initialize)中进行定义。

定义

schedule_function(func, date_rule, time_rule)

周期执行调度函数,在符合date_rule, time_rule下周期性执行,需在 initialize 函数中调用。如果initialize中调用了schedule_function函数,那么算法会按照date_rule和time_rule周期性执行func函数,如果没有调用schedule_function函数,则算法会每个单位时间调用handle_data函数。

参数:

func – 被调用函数,包含两个参数 context 和 data,同handle_data
date_rule – 日期规则,通过date_rules下的方法可获取,后面会给出具体详情
time_rule – 时间规则,通过time_rules下的方法可获取,后面会给出具体详情

月度执行调度模型(month_start和month_end)正如字面含义,决定了在哪些日期点上运行,比如month_start表明在每个月月初第一天运行func,month_end表明在月末最后一天运行func。
可以传入参数——days_offset,默认该参数为0,该参数可以理解成一个时间偏移量,month_start(1)表明在该月第二个交易日运行func,依此类推,如果该参数超过当月交易日,那么该函数在该月就不会运行func。

示例代码

def initialize(context):
    schedule_function(func=rebalance,
        date_rule=date_rules.week_start(),   # 例如每周开始时执行一次
        time_rule=time_rules.market_open(hours=1))   # 开盘后一个小时执行

def rebalance(context, data):
    # 策略交易逻辑和订单生成在此部分构建,代替了handle_data函数
    pass

下面是日期规则和时间规则的两幅截图:


日期规则
定义

date_rule

在固定的日期规则下调用func函数,func为被调用函数,func函数需要传入context和data,用法同handle_data

示例代码

# 每天调用一次func函数
schedule_function(func=rebalance, date_rule=date_rules.every_day())

# 每周开始时调用一次func函数,默认为days_offset=0,该周交易日的第一天调用
schedule_function(func=rebalance, date_rule=date_rules.week_start())

# 每周开始时调用一次func函数,days_offset=1表示该周交易日的第二天调用,以此类推
schedule_function(func=rebalance, date_rule=date_rules.week_start(days_offset=1))

# 每月开始时调用一次func函数,days_offset默认为0,表示每个月交易日的第一天
schedule_function(func=rebalance, date_rule=date_rules.month_start())

# 每周结束时调用一次func函数,days_offset=0表示该周交易日的最后一天调用
schedule_function(func=rebalance, date_rule=date_rules.week_end())

# 每月结束时调用一次func函数,days_offset默认为0,表示每个月交易日的最后一天
schedule_function(func=rebalance, date_rule=date_rules.month_end())

时间规则

定义

time_rule

在固定的时间规则下调用func函数

market_open表明开盘时调用func,如果没有偏移,默认为开盘后一分钟调用func

market_close表明收盘时调用func,如果没有偏移,默认为收盘前两分钟调用func

示例代码

# 每日开盘后1小时30分钟调用func
schedule_function(func=rebalance, date_rules.every_day(), time_rule=time_rules.market_open(hours=1, minutes=30))
# 每日收盘前5分钟调用func
schedule_function(func=rebalance, date_rules.every_day(), time_rule=time_rules.market_close(minutes=5))

# 多个周期执行调度函数

可以使用多个周期执行调度函数,比如在月初和月中都调用下换仓函数。
示例代码

def initialize(context):
  # 在每个月的第二个交易日执行
  schedule_function(
    rabalance,
    date_rules.month_start(days_offset=1)
  )

  # 在每个月的第十个交易日执行
  schedule_function(
    rebalance,
    date_rules.month_start(days_offset=9)
  )

def myfunc(context,data):
    pass