Zipline有六種下單函數:¶
order():購買指定股數。
order_value():購買指定價值的股票。
order_percent():購買指定價值(整個投資組合價值(portfolio value)的一個特定比例)的股票。
order_target():交易直到該股票帳上總股數達到指定數量為止。
order_target_value():交易到帳上該股票價值達到指定價值為止。
order_target_percent():將股票在投資組合的比重調整到指定的比例。
本篇將會介紹
order_value()
以及order_target_value()
的使用辦法。¶本文件包含以下兩個部份:
閱讀本篇之前請先閱讀:¶
Zipline Order (order & order_target).ipynb
閱讀本篇之後可以搭配閱讀:¶
使用前記得先做 import:from zipline.api import order_value, order_target_value
或from zipline.api import *
。
* order_value:買賣指定價值的股票。
zipline.api.order_value(asset, value, limit_price=None, stop_price=None, style=None)
value:float股票價值
order_target_value:透過買/賣使手上股票在下單當下*的市場價值達到指定目標。
zipline.api.order_target_value(asset, target, limit_price=None, stop_price=None, style=None)
target:float目標價值
stop_price
及limit_price
皆是和下一個交易日的收盤價做比較。order_value
與order_target_value
運作邏輯類似,都是以下單日的收盤價做基準,將下單的價值轉換為 amount。實際交易時再用當天的收盤價,和下單時算出的 amount 成交,所以下單時指定的價值不一定會與實際成交時價值一致。stop_price
及limit_price
會在除權日進行調整。stop_price
及limit_price
都是原本數值乘以 ratio(也就是僅除權的調整係數) 並 round 到小數以下第二位。(僅除權的調整係數使用 TEJ API 的 TWN/APIPRCD(交易資料-股價資料)中的 adjfac_a 欄位進行計算)handle_data
階段使用且不得在before_trading_start
階段使用。import pandas as pd
import numpy as np
import datetime
import tejapi
import time
import os
import warnings
warnings.filterwarnings('ignore')
# tej_key
tej_key ='your key'
tejapi.ApiConfig.api_key = tej_key
os.environ['TEJAPI_BASE'] = "https://api.tej.com.tw"
os.environ['TEJAPI_KEY'] = tej_key
# date
# set date
start='2022-09-16'
end='2022-10-16'
os.environ['mdate'] = '20220916 20221016'
tz = 'UTC'
start_dt, end_dt = pd.Timestamp(start, tz = tz), pd.Timestamp(end, tz = tz)
# calendar
calendar_name='TEJ'
# bundle_name
bundle_name = 'tquant'
os.environ['ticker'] = '2603 IR0001'
!zipline ingest -b tquant
Merging daily equity files:
[2023-11-30 07:51:09.233092] INFO: zipline.data.bundles.core: Ingesting tquant.
from zipline.api import *
from zipline import run_algorithm
from zipline.finance import commission, slippage
from zipline.utils.calendar_utils import get_calendar
from zipline.utils.run_algo import get_transaction_detail
在回測的第一個時間點(context.i 為 0,2022-09-16),購買context.asset
中每檔股票,每檔價值皆為 40000 元。
if context.i == 0:
for asset in context.asset:
order_value(asset, 40000)
在回測的第十一個時間點(context.i 為 10,2022-09-30),對投資組合中的每檔股票進行調整,使其帳上持有的價值達到 60000 元。
if context.i == 10:
for asset in context.asset:
order_target_value(asset, 60000)
在回測的第十六個時間點(context.i 為 15,2022-10-07),對投資組合中的每檔股票進行調整,使其帳上持有的價值減少到 30000 元。
if context.i == 15:
for asset in context.asset:
order_target_value(asset, 30000)
最後,使用 record 函數記錄每檔股票的收盤價。然後遞增 context.i 的值,表示回測已進入下一個時間點。
record(close=data.current(context.asset, 'close'))
context.i += 1
記錄每檔股票的收盤價,並將 context.i
遞增 1,表示回測進入下一個時間點。
def initialize(context):
context.i = 0
context.tickers = ['2603']
context.asset = [symbol(ticker) for ticker in context.tickers]
set_slippage(slippage.FixedSlippage(spread=0.00))
set_commission(commission.PerDollar(cost=commission_cost))
set_benchmark(symbol('IR0001'))
def handle_data(context, data):
if context.i == 0: # 2022-09-16
for asset in context.asset:
order_value(asset, 40000)
if context.i == 10: # 2022-09-30
for asset in context.asset:
order_target_value(asset, 60000)
if context.i == 15: # 2022-10-07
for asset in context.asset:
order_target_value(asset, 30000)
record(close=data.current(context.asset, 'close'))
context.i += 1
commission_cost = 0.001425 + 0.003 / 2 # commission + tax
capital_base = 1e6
closing_price = tejapi.fastget('TWN/APIPRCD',
coid=['2603'],
opts={'columns':['mdate','coid','close_d']},
mdate={'gte':start_dt,'lte':end_dt },
paginate=True)
performance = run_algorithm(start=start_dt,
end=end_dt,
initialize=initialize,
handle_data=handle_data,
capital_base=capital_base,
trading_calendar=get_calendar(calendar_name),
bundle=bundle_name)
positions, transactions, orders = get_transaction_detail(performance)
closing_price.query('mdate <= "2022-09-19"')
mdate | coid | close_d | |
---|---|---|---|
0 | 2022-09-16 | 2603 | 80.8 |
1 | 2022-09-19 | 2603 | 169.0 |
orders.loc['2022-09-16':'2022-09-19']
sid | symbol | id | dt | reason | created | amount | filled | commission | stop | limit | stop_reached | limit_reached | asset | status | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
2022-09-16 13:30:00+08:00 | 0 | 2603 | df7703877ef749c9af6551dd24992a8b | 2022-09-16 13:30:00+08:00 | None | 2022-09-16 13:30:00+08:00 | 495 | 0 | 0.000000 | None | None | False | False | Equity(0 [2603]) | 0 |
2022-09-19 13:30:00+08:00 | 0 | 2603 | df7703877ef749c9af6551dd24992a8b | 2022-09-19 13:30:00+08:00 | None | 2022-09-16 13:30:00+08:00 | 213 | 213 | 105.291225 | None | None | False | False | Equity(0 [2603]) | 1 |
positions.loc['2022-09-30']
sid | symbol | asset | amount | cost_basis | last_sale_price | |
---|---|---|---|---|---|---|
2022-09-30 13:30:00+08:00 | 0 | 2603 | Equity(0 [2603]) | 213 | 169.494325 | 146.0 |
下一個交易日(10/3)成交。
orders.loc['2022-09-30':'2022-10-03']
sid | symbol | id | dt | reason | created | amount | filled | commission | stop | limit | stop_reached | limit_reached | asset | status | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
2022-09-30 13:30:00+08:00 | 0 | 2603 | 81259ca0671a45a9b447e597843195f6 | 2022-09-30 13:30:00+08:00 | None | 2022-09-30 13:30:00+08:00 | 197 | 0 | 0.000000 | None | None | False | False | Equity(0 [2603]) | 0 |
2022-10-03 13:30:00+08:00 | 0 | 2603 | 81259ca0671a45a9b447e597843195f6 | 2022-10-03 13:30:00+08:00 | None | 2022-09-30 13:30:00+08:00 | 197 | 197 | 86.145637 | None | None | False | False | Equity(0 [2603]) | 1 |
positions.loc['2022-10-07']
sid | symbol | asset | amount | cost_basis | last_sale_price | |
---|---|---|---|---|---|---|
2022-10-07 13:30:00+08:00 | 0 | 2603 | Equity(0 [2603]) | 410 | 160.097407 | 155.5 |
orders.query('created.dt.strftime("%Y-%m-%d") == "2022-10-07"')
sid | symbol | id | dt | reason | created | amount | filled | commission | stop | limit | stop_reached | limit_reached | asset | status | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
2022-10-07 13:30:00+08:00 | 0 | 2603 | 4321592d8ad044e3948c70129676e278 | 2022-10-07 13:30:00+08:00 | None | 2022-10-07 13:30:00+08:00 | -217 | 0 | 0.0000 | None | None | False | False | Equity(0 [2603]) | 0 |
2022-10-11 13:30:00+08:00 | 0 | 2603 | 4321592d8ad044e3948c70129676e278 | 2022-10-11 13:30:00+08:00 | None | 2022-10-07 13:30:00+08:00 | -217 | -217 | 99.0171 | None | None | False | False | Equity(0 [2603]) | 1 |
run_algorithm
產出的報表)中的 long_exposure
(帳上股票長部位市值)可以看到,在10/3第一次調整,目標是 6 萬元,但因為10/3市價相對於下單日(9/30)提高了,然而還是會用9/30算出來的 amount 去買,因此 long_exposure 超出了目標。performance[['period_open','long_exposure']].query('period_open.dt.strftime("%Y-%m-%d") in ["2022-10-03","2022-10-11"]')
period_open | long_exposure | |
---|---|---|
2022-10-03 13:30:00+08:00 | 2022-10-03 09:01:00+08:00 | 61295.0 |
2022-10-11 13:30:00+08:00 | 2022-10-11 09:01:00+08:00 | 30108.0 |
closing_price
mdate | coid | close_d | |
---|---|---|---|
0 | 2022-09-16 | 2603 | 80.8 |
1 | 2022-09-19 | 2603 | 169.0 |
2 | 2022-09-20 | 2603 | 172.5 |
3 | 2022-09-21 | 2603 | 166.5 |
4 | 2022-09-22 | 2603 | 156.0 |
5 | 2022-09-23 | 2603 | 158.0 |
6 | 2022-09-26 | 2603 | 145.5 |
7 | 2022-09-27 | 2603 | 153.5 |
8 | 2022-09-28 | 2603 | 144.0 |
9 | 2022-09-29 | 2603 | 144.0 |
10 | 2022-09-30 | 2603 | 146.0 |
11 | 2022-10-03 | 2603 | 149.5 |
12 | 2022-10-04 | 2603 | 150.5 |
13 | 2022-10-05 | 2603 | 155.5 |
14 | 2022-10-06 | 2603 | 155.5 |
15 | 2022-10-07 | 2603 | 155.5 |
16 | 2022-10-11 | 2603 | 156.0 |
17 | 2022-10-12 | 2603 | 147.0 |
18 | 2022-10-13 | 2603 | 144.5 |
19 | 2022-10-14 | 2603 | 152.5 |