更多精彩内容详见个人量化交易专辑索引
1. 启动前回调,添加交易账户,订阅行情,策略初始化计算等
def pre_start(context):
context.log.info("pre_start")
# 确定日期
today = datetime.datetime.now().strftime('%Y-%m-%d')
context.log.warning("today: {}".format(today))
private['today'] = today
# 绑定账户
context.add_account(SOURCE, ACCOUNT, 2000000.0)
# 无法在订阅阶段获取账户持仓情况,因此先要更新当天的subscribe.txt
try:
# 按subscribe.txt订阅股票
subscribe_dict = json.load(open(SUBSCRIBE_FILE,'r'))
if subscribe_dict['date'] == today:
for exchange_id in subscribe_dict['exchange']:
if len(subscribe_dict['exchange'][exchange_id]) > 0:
context.subscribe(SOURCE, subscribe_dict['exchange'][exchange_id], exchange_id)
except:
context.log.warning("loss subscribe")
2. 启动准备工作完成后回调,策略只能在本函数回调以后才能进行获取持仓和报单
def post_start(context):
context.log.info("post_start")
today = private['today']
# 读取账户资金、持仓
book = context.get_account_book(SOURCE, ACCOUNT)
asset = book.asset
context.log.warning("crash: {}".format(asset.avail))
positions = book.long_positions
for key in positions:
pos = positions[key]
context.log.warning("(instrument_id){} (direction){} (volume){} (yesterday_volume){}".format(
pos.instrument_id, pos.direction, pos.volume, pos.yesterday_volume))
# 生成账本
try:
os.remove(BOOK_FILE)
except:
if os.path.exists(BOOK_FILE):
context.log.error("{} remove failed".format(BOOK_FILE))
return
private['book'] = {}
private['book']['crash'] = asset.avail
private['book']['hold_dict'] = {}
for key in positions:
pos = positions[key]
if pos.volume == 0:
continue
private['book']['hold_dict'][pos.instrument_id] = {
'buy_date':datetime.datetime.strptime(pos.trading_day, '%Y%m%d').strftime('%Y-%m-%d'),
'orig_share':pos.volume,
'cost':pos.position_cost_price*pos.volume,
}
json.dump(private['book'], open(BOOK_FILE, 'w'))
# 量化选股,确定买入、卖出股票具体份额
try:
os.remove(TRADE_FILE)
except:
if os.path.exists(TRADE_FILE):
context.log.error("{} remove failed".format(TRADE_FILE))
return
child = subprocess.Popen(['trade-assistant.bat'], stdout=subprocess.PIPE)
child.wait()
try:
trade = json.load(open(TRADE_FILE,'r'))
except:
context.log.warning("loss trade")
return
for code in trade['sale_codes']:
trade['sale_codes'][code]['on_ask'] = 0
for code in trade['buy_codes']:
trade['buy_codes'][code]['on_bid'] = 0
private['trade'] = trade
# 生成订阅标的subscribe.txt
try:
os.remove(SUBSCRIBE_FILE)
except:
if os.path.exists(SUBSCRIBE_FILE):
context.log.error("{} remove failed".format(SUBSCRIBE_FILE))
return
subscribe_dict = {}
subscribe_dict['date'] = today
subscribe_dict['exchange'] = {}
subscribe_dict['exchange'][Exchange.SSE] = []
subscribe_dict['exchange'][Exchange.SZE] = []
for code in trade['sale_codes']:
market = trade['sale_codes'][code]['market']
if market == 'SH':
subscribe_dict['exchange'][Exchange.SSE].append(code)
elif market == 'SZ':
subscribe_dict['exchange'][Exchange.SZE].append(code)
for code in trade['buy_codes']:
market = trade['buy_codes'][code]['market']
if market == 'SH':
subscribe_dict['exchange'][Exchange.SSE].append(code)
elif market == 'SZ':
subscribe_dict['exchange'][Exchange.SZE].append(code)
json.dump(subscribe_dict, open(SUBSCRIBE_FILE, 'w'))
3. 收到快照行情时回调,行情信息通过 quote 对象获取
def on_quote(context, quote):
#context.log.info("[on_quote] {}".format(quote))
code = quote.instrument_id
sale_codes = private['trade']['sale_codes']
buy_codes = private['trade']['buy_codes']
# 尝试分批买入、卖出,直到份额用完
if code in sale_codes:
ask_share = sale_codes[code]['ask_share']
if ask_share > 0:
order_id = context.insert_order(code, Exchange.SSE, ACCOUNT, quote.last_price, ask_share, PriceType.Limit, Side.Sell, Offset.Open)
if order_id > 0:
sale_codes[code]['on_ask'] += ask_share
sale_codes[code]['ask_share'] = 0
context.log.info("[order] (order_id){} (code){} (ask_share){}".format(order_id, code, ask_share))
# 通过添加时间回调,在三秒以后撤单
context.add_timer(context.now() + 3 * 1000000000, lambda ctx, event: cancel_order(ctx, order_id))
if code in buy_codes:
bid_share = buy_codes[code]['bid_share']
if bid_share > 0:
order_id = context.insert_order(code, Exchange.SSE, ACCOUNT, quote.last_price, bid_share, PriceType.Limit, Side.Buy, Offset.Open)
if order_id > 0:
buy_codes[code]['on_bid'] += bid_share
buy_codes[code]['bid_share'] = 0
context.log.info("[order] (order_id){} (code){} (bid_share){}".format(order_id, code, bid_share))
# 通过添加时间回调,在三秒以后撤单
context.add_timer(context.now() + 3 * 1000000000, lambda ctx, event: cancel_order(ctx, order_id))
4. 收到订单状态回报时回调
def on_order(context, order):
#context.log.info("[on_order] {}".format(order))
code = order.instrument_id
sale_codes = private['trade']['sale_codes']
buy_codes = private['trade']['buy_codes']
if (order.status == OrderStatus.Cancelled) or \
(order.status == OrderStatus.Error) or \
(order.status == OrderStatus.PartialFilledNotActive):
if code in sale_codes:
sale_codes[code]['ask_share'] += order.volume_left
sale_codes[code]['on_ask'] -= order.volume_left
elif code in buy_codes:
buy_codes[code]['bid_share'] += order.volume_left
buy_codes[code]['on_bid'] -= order.volume_left
5. 收到成交信息回报时回调
def on_trade(context, trade):
#context.log.info("[on_trade] {}".format(trade))
code = trade.instrument_id
sale_codes = private['trade']['sale_codes']
buy_codes = private['trade']['buy_codes']
if code in sale_codes:
sale_codes[code]['on_ask'] -= trade.volume
elif code in buy_codes:
buy_codes[code]['on_bid'] -= trade.volume
6. 自定义撤单回调函数
def cancel_order(context, order_id):
action_id = context.cancel_order(order_id)