# 导入函数库 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[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