| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- # 导入函数库
- from jqdata import *
- import pandas as pd
- import numpy as np
- import datetime
- import re
- ## 初始化函数,设定基准等等
- def initialize(context):
- # 设定沪深300作为基准
- set_benchmark('000300.XSHG')
- # 开启动态复权模式(真实价格)
- set_option('use_real_price', True)
- # 过滤掉order系列API产生的比error级别低的log
- # log.set_level('order', 'error')
- # 输出内容到日志 log.info()
- log.info('初始函数开始运行且全局只运行一次')
- ### 期货相关设定 ###
- # 设定账户为金融账户
- set_subportfolios([SubPortfolioConfig(cash=context.portfolio.starting_cash, type='index_futures')])
- # 期货类每笔交易时的手续费是:买入时万分之0.23,卖出时万分之0.23,平今仓为万分之23
- set_order_cost(OrderCost(open_commission=0.000023, close_commission=0.000023,close_today_commission=0.0023), type='index_futures')
- # 设定保证金比例
- set_option('futures_margin_rate', 0.15)
- # 设置期货交易的滑点
- set_slippage(StepRelatedSlippage(2))
- # 运行函数(reference_security为运行时间的参考标的;传入的标的只做种类区分,因此传入'IF8888.CCFX'或'IH1602.CCFX'是一样的)
- # 注意:before_open/open/close/after_close等相对时间不可用于有夜盘的交易品种,有夜盘的交易品种请指定绝对时间(如9:30)
-
- # 合约乘数
- g.multiplier = {
- 'A': 10, 'AG': 15, 'AL': 5, 'AO': 20, 'AP': 10, 'AU': 1000, 'B': 10,
- 'BC': 5, 'BR': 5, 'BU': 10, 'C': 10, 'CF': 5, 'CJ': 5, 'CS': 10,
- 'CU': 5, 'CY': 5, 'EB': 5, 'EC': 50, 'EG': 10, 'FG': 20, 'FU': 10,
- 'HC': 10, 'I': 100, 'J': 60, 'JD': 5, 'JM': 100,
- 'L': 5, 'LC': 1, 'LH':16, 'LR': 0.05, 'LU': 10, 'M': 10, 'MA': 10, 'NI': 1, 'NR': 10, 'OI': 10,
- 'P': 10, 'PB': 5, 'PF': 5, 'PG': 20, 'PK': 5,
- 'PP': 5, 'RB': 10, 'RI': 0.05, 'RM': 10, 'RU': 10,
- 'SA': 20, 'SC': 1000, 'SF': 5, 'SH': 30, 'SI': 5, 'SM': 5, 'SN': 1, 'SP': 10, 'SR': 10,
- 'SS': 5, 'TA': 5, 'UR': 20, 'V': 5,
- 'Y': 10, 'ZC': 0.05, 'ZN': 5}
- # 设置AU期货为交易标的
- g.security = get_dominant_future('AU')
-
- # 设置交易参数
- g.position_size = 0.8 # 仓位比例
- g.is_traded = False # 当日是否已交易标志
-
- # 设置明确买入日期,格式为YYYY-MM-DD字符串列表
- g.buy_dates = ['2020-08-10']
-
- # 设置止损参数
- g.stop_loss_pct = 0.05 # 止损比例,5%
-
- # 设置自动换月参数
- g.days_before_expiry = 5 # 到期前多少天换月
-
- # 用于收益跟踪的变量
- g.trade_records = [] # 交易记录
- g.initial_positions = {} # 初始持仓信息
- g.last_roll_date = None # 上次换月日期
- g.switch_records = [] # 换月交易记录
-
- # 开盘前运行
- run_daily( before_market_open, time='09:00', reference_security='IF8888.CCFX')
- # 开盘时运行
- run_daily( market_open, time='09:30', reference_security='IF8888.CCFX')
- # 每天14:30检查是否需要换月(仅在持有仓位时运行)
- run_daily(check_and_switch_position, time='14:30', reference_security='IF8888.CCFX')
- # 收盘后运行
- run_daily( after_market_close, time='15:30', reference_security='IF8888.CCFX')
- ## 开盘前运行函数
- def before_market_open(context):
- # 更新主力合约
- g.security = get_dominant_future('AU')
- # log.info("当前主力合约: {}".format(g.security))
- ## 开盘时运行函数
- def market_open(context):
- # 如果是买入日期且没有持仓,则买入
- if is_buy_date(context) and not has_position(context) and not g.is_traded:
- buy_contract(context)
- def is_buy_date(context):
- current_date = context.current_dt.date()
- date_str = current_date.strftime('%Y-%m-%d')
-
- # 检查当前日期是否在买入日期列表中
- if date_str in g.buy_dates:
- log.info("今天 {} 是设定的买入日期".format(date_str))
- return True
- return False
-
- # 买入合约
- def buy_contract(context):
- # 选择子账户
- subportfolio = context.subportfolios[0]
-
- # 计算买入数量
- cash_to_use = subportfolio.available_cash * g.position_size
- price = get_price(g.security, count=1, fields='close')['close'][0]
- # contract_multiplier = get_contract_multiplier(g.security)
- # margin_rate = get_future_margin_rate(g.security)
-
- # 计算最多可买入的手数
- # max_amount = int(cash_to_use / (price * contract_multiplier * margin_rate))
- max_amount = 1
-
- if max_amount > 0:
- # 买入合约
- order(g.security, max_amount, side='long')
- log.info("买入 {} 合约,数量: {} 手".format(g.security, max_amount))
-
- # 记录交易信息用于收益跟踪
- g.trade_records.append({
- 'date': context.current_dt.strftime('%Y-%m-%d'),
- 'action': 'buy',
- 'contract': g.security,
- 'price': price,
- 'amount': max_amount
- })
-
- g.is_traded = True
- # 检查是否有持仓
- def has_position(context):
- for subportfolio in context.subportfolios:
- positions = list(subportfolio.long_positions.keys()) + list(subportfolio.short_positions.keys())
- # log.info(f'当前持仓为: {positions}')
- if len(positions) > 0:
- return True
- return False
-
- # 检查是否换月
- def check_and_switch_position(context):
- """每天14:30检查持仓并执行换月操作"""
- if has_position(context):
- # log.info("检测到持仓,开始执行换月检查...")
- position_auto_switch(context)
- else:
- log.info("无持仓,跳过换月检查")
- ## 收盘后运行函数
- def after_market_close(context):
- # 得到当天所有成交记录
- trades = get_trades()
- for _trade in trades.values():
- log.info('成交记录:'+str(_trade))
- log.info('##############################################################')
- ########################## 获取期货合约信息,请保留 #################################
- # 获取金融期货合约到期日
- def get_CCFX_end_date(future_code):
- # 获取金融期货合约到期日
- return get_security_info(future_code).end_date
- ########################## 自动移仓换月函数 #################################
- def position_auto_switch(context,pindex=0,switch_func=None, callback=None):
- """
- 期货自动移仓换月。默认使用市价单进行开平仓。
- :param context: 上下文对象
- :param pindex: 子仓对象
- :param switch_func: 用户自定义的移仓换月函数.
- 函数原型必须满足:func(context, pindex, previous_dominant_future_position, current_dominant_future_symbol)
- :param callback: 移仓换月完成后的回调函数。
- 函数原型必须满足:func(context, pindex, previous_dominant_future_position, current_dominant_future_symbol)
- :return: 发生移仓换月的标的。类型为列表。
- """
- import re
- subportfolio = context.subportfolios[pindex]
- symbols = set(subportfolio.long_positions.keys()) | set(subportfolio.short_positions.keys())
- switch_result = []
- for symbol in symbols:
- match = re.match(r"(?P<underlying_symbol>[A-Z]{1,})", symbol)
- if not match:
- raise ValueError("未知期货标的:{}".format(symbol))
- else:
- dominant = get_dominant_future(match.groupdict()["underlying_symbol"])
- cur = get_current_data()
- # log.info(f'主力合约:{dominant},持仓:{symbol}')
- symbol_last_price = cur[symbol].last_price
- dominant_last_price = cur[dominant].last_price
- if dominant > symbol:
- log.debug(f'需要换月')
- for positions_ in (subportfolio.long_positions, subportfolio.short_positions):
- if symbol not in positions_.keys():
- continue
- else :
- p = positions_[symbol]
- if switch_func is not None:
- switch_func(context, pindex, p, dominant)
- else:
- amount = p.total_amount
- # 跌停不能开空和平多,涨停不能开多和平空。
- if p.side == "long":
- symbol_low_limit = cur[symbol].low_limit
- dominant_high_limit = cur[dominant].high_limit
- if symbol_last_price <= symbol_low_limit:
- log.warning("标的{}跌停,无法平仓。移仓换月取消。".format(symbol))
- continue
- elif dominant_last_price >= dominant_high_limit:
- log.warning("标的{}涨停,无法开仓。移仓换月取消。".format(symbol))
- continue
- else:
- log.info("进行移仓换月:({0},long) -> ({1},long)".format(symbol, dominant))
- close_order =order_target(symbol,0,side='long')
- open_order = order_target(dominant,amount,side='long')
- # log.info(f'close_order: {close_order}')
- # log.info(f'open_order: {open_order}')
-
- # 记录换月交易信息
- key_symbol = re.match(r"([A-Z]+)", symbol).group(1)
- contract_multiplier = g.multiplier[key_symbol]
- close_money = close_order.amount * close_order.price * contract_multiplier
- open_money = open_order.amount * open_order.price * contract_multiplier
- switch_cost = abs(close_money - open_money)
- log.info(f'contract_multiplier: {contract_multiplier}, close_money: {close_money}, open_money: {open_money}, switch_cost: {switch_cost}')
-
- switch_record = {
- 'date': context.current_dt.strftime('%Y-%m-%d'),
- 'close_contract': symbol,
- 'close_price': close_order.price,
- 'close_money': close_money,
- 'open_contract': dominant,
- 'open_price': open_order.price,
- 'open_money': open_money,
- 'switch_cost': switch_cost,
- 'side': 'long'
- }
- g.switch_records.append(switch_record)
- log.info("换月记录:{}".format(switch_record))
- # 计算总换月次数和总换月成本
- total_switches = len(g.switch_records)
- total_cost = sum(record['switch_cost'] for record in g.switch_records)
- log.info(f"总换月次数: {total_switches}, 总换月成本: {total_cost}")
-
- switch_result.append({"before": symbol, "after":dominant, "side": "long"})
- if callback:
- callback(context, pindex, p, dominant)
- if p.side == "short":
- symbol_high_limit = cur[symbol].high_limit
- dominant_low_limit = cur[dominant].low_limit
- if symbol_last_price >= symbol_high_limit:
- log.warning("标的{}涨停,无法平仓。移仓换月取消。".format(symbol))
- continue
- elif dominant_last_price <= dominant_low_limit:
- log.warning("标的{}跌停,无法开仓。移仓换月取消。".format(symbol))
- continue
- else:
- log.info("进行移仓换月:({0},short) -> ({1},short)".format(symbol, dominant))
- close_order = order_target(symbol,0,side='short')
- open_order = order_target(dominant,amount,side='short')
- # log.info(f'close_order: {close_order}')
- # log.info(f'open_order: {open_order}')
-
- # 记录换月交易信息
- key_symbol = re.match(r"([A-Z]+)", symbol).group(1)
- contract_multiplier = g.multiplier[key_symbol]
- close_money = close_order.amount * close_order.price * contract_multiplier
- open_money = open_order.amount * open_order.price * contract_multiplier
- switch_cost = abs(close_money - open_money)
- log.info(f'contract_multiplier: {contract_multiplier}, close_money: {close_money}, open_money: {open_money}, switch_cost: {switch_cost}')
-
- switch_record = {
- 'date': context.current_dt.strftime('%Y-%m-%d'),
- 'close_contract': symbol,
- 'close_price': close_order.price,
- 'close_money': close_money,
- 'open_contract': dominant,
- 'open_price': open_order.price,
- 'open_money': open_money,
- 'switch_cost': switch_cost,
- 'side': 'short'
- }
- g.switch_records.append(switch_record)
- log.info("换月记录:{}".format(switch_record))
- # 计算总换月次数和总换月成本
- total_switches = len(g.switch_records)
- total_cost = sum(record['switch_cost'] for record in g.switch_records)
- log.info(f"总换月次数: {total_switches}, 总换月成本: {total_cost}")
-
- switch_result.append({"before": symbol, "after": dominant, "side": "short"})
- if callback:
- callback(context, pindex, p, dominant)
- return switch_result
|