前言

  因子选股是近几年比较热门的量化选股方法,其本质是找出与证券收益密切相关的因子,通过这些因子定量筛选具有高收益能力的证券构建动态股票池。由此一来,就将对几千只证券的收益-风险预测转化成为对几个因子的收益-风险预测,极大的降低了选股的工作量,同时也提高了量化的准确度。

  软件提供专业的单因子、多因子分析平台,囊括了500多种市场细分因子,用户还可以根据自身研究合成自定义因子分析,借助专业的分析平台,快速锁定因子选股策略。

优势

  • 分层回测/IC分析/市值分位,全方位检验单因子的收益性和持续性
  • 多因子分析检验因子相关性,合成大类因子
  • 按因子成分大小构建多因子组合,对全市场证券筛选、排序,打分选股

案例

案例一:单因子分析,确定因子池

单因子是多因子分析的基石,在因子分析过程中极为重要,因为它不仅关乎一个因子能否进入因子池,还有助于我们全方位了解因子特点,为后续多因子模型的建立打下基础。所以我们需要先在每个大类因子中测试单个细分因子的效果,筛选出该大类因子中效果较好的单个因子,构建因子池。


第一步:利用排名分析检验单因子有效性


从经济理论上来说,市盈率和证券的收益密切相关,一些投资者认为低市盈率的股票处于价值低估状态,后市往往可能会有上涨空间。因此,我们在财务指标因子大类下找到年市盈率指标,加入到排名条件,利用排名分析功能对该因子的有效性进行实证分析,如下图。



点击上图⑤【开始计算】,软件便在云端对全市场几千只证券扫描计算,4秒钟就可以出具近4年历史数据轮动选股的全面的分析报告(如上图⑦),普通选股工具4秒钟恐怕还没有遍历完历史数据!


第二步:分析报告多维度解读因子有效性


1、分层回测

分层回测是指将股票池中的股票按照排名分进行排序,按照排名分为5段、10段或更多段,计算每段中的股票总收益,并输出年化收益柱状对比图和累计收益曲线,便于投资者直观的检查是否排名越高的分段收益越好,以此检验因子的有效性。


(1)年化收益对比图:

如下图,按排名将股票分为5段,从低档位到高档位依次排列。投资者可根据分段的收益排列程度初步判断因子的显著性,如年化收益图高低排列整齐,则说明因子有效性较强,排名高的股票更可能产生超额收益。还可结合因子IC均值、因子IR值、每段平均股数、排列单调性和换手率指标,具体综合评估因子的稳定性和投资意义大小。



(2)累计收益曲线:

如下图,充分展示了各个分段的股票收益在时间截面的变化,便于用户获取更加详细的收益变化信息。



2、IC分析

IC值即信息系数,代表因子预测股票收益的能力,是每个时间截点上因子在各个股票的暴露度和股票下一期收益的相关系数。一般来说,IC值越大,则说明因子和收益率相关程度越大,因子的预测性越高,选股的效果就越好。


(1)因子历史IC:

由于市场风格是会轮动的,IC值可能会切换正负号,所以在选择因子时还要计算相关系数的正负比例,并选择比例高的方向。因此投资者需要根据IC为正的概率和IC均值大小综合评估因子的有效性强弱。如下图,IC均值>0.03,则认为排序的因子是有效的,并且IC为正的概率较高,说明因子方向也是正确的。



(2)因子IC/IR衰减:

因子的有效性是有时效的,一个有效的因子,应当在较长的时间段内持续有效,而不是仅仅在一段时间内有效。因子IC衰退通过观察随着滞后时间的延长,检验因子有效性降低的速度,如下图,因子IC的衰减速度较慢,说明其对交易延期不敏感,因子的持续性越强。



3、市值分位图:

市值是影响收益的一个重要因素,很多指标都会受到市值的影响,但如果一个多因子组合中有过多的重复信息,就会导致我们的选股结果比较集中,增大收益波动率,进而削弱稳定性。因此我们在使用这些因子之前,还需要对它们是否对市值有偏好进行检验和处理。如果因子暴露度表现出与市值有着明显相关性,则使用因子时就要对其进行市值中性化处理。


如下图,排名分与市值呈现高低分布排列,则说明因子和市值之间存在较强的相关性,需要对因子进行市值中性化处理后才可以使用。



重复以上单因子检验操作,对其他大类下的细分因子进行检验,即可初步确定因子池。

案例二:多因子分析,组建选股策略

通过对细分因子的筛选和归纳,我们初步选出了与收益率显著相关的因子池。但我们并不能直接用这些因子来选股交易,因为此时的因子仍是我们主观定义的,它们之间可能具有很强的相关性,如果不做处理就用于选股,可能在同类因子上暴露过多的风险!所以要对因子池进行整合处理。

以往的多因子分析仅仅是对将所选定的因子进行简单的归总,但用一个细分因子去代替大类因子选股,总是有一定的随意性在其中,因此我们要对这些细分因子进行量化检验和加权处理,针对性的合成选股因子。


第一步:细分因子相关性检验

单因子检验确定的因子池中,有很多因子来自同一大类因子下,例如,动态市盈率和滚动市盈率,虽然都通过了单因子分析但它们是同属于财务指标因子下的估值因子,可能会存在一些相关性。因此我们将两个因子选入排名条件,利用【因子相关性】来检验其相关程度,如下图。



点击上图⑤【开始计算】,软件在云端高速运算,按照自定义的调仓周期对历史数据进行轮动选股计算,并秒速提供分析报告。


1、IC时间序列分析:

如下图,通过对各因子在各个时点上的IC趋势值大小进行横向比较,展示因子间的相关性强弱。投资者可结合具体的时间节点对因子的趋近和偏离程度综合分析。



2、IC因子总结:

对各个因子的当期IC、12周期IC均值、IC均值、IR进行横向比较,如下图。



3、因子IC相关性:

计算因子12周期IC均值的皮尔森系数,因子间的相关程度一目了然。如下图,两个因子之间的皮尔森系数高达0.975,说明因子间存在着极强的相关性!



第二步:细分因子合成 - 确定有效因子

通过第一步对因子间的相关性进行分析,我们发现所选的因子之间存在着极强的相关性,那么对于这种因子集合,我们该如何处理呢?

对于相关度高的因子有两种常见处理方法,一种是根据因子有效性进行排序,挑选最有效的因子进行保留,删除其他因子。另一种是对因子集合进行合成,这样可以尽可能多的保留有效因子信息。下面我们仍以动态市盈率和滚动市盈率因子为例,详细说明细分因子的合成思路。


(一)确定各因子的合成权重

因子合成的权重有三种形式:
1、等权重处理
即对两个排名因子等权重分配,动态市盈率和滚动市盈率各0.5权重进行合成。
2、IC均值加权处理
此方法考虑到因子有效性的差异,对表现更显著的因子,分配予更高的权重。
①进行因子相关性分析,以12周期IC均值计算权重大小。如下图:



② 为了保证因子的权重和为1,需要做归一化处理,如下:
动态市盈率权重 = 0.013/(0.013+0.013) = 0.5
滚动市盈率权重 = 0.013/(0.013+0.013) = 0.5
3、IR加权处理
此方法基于收益-风险这一基本准则,综合考虑因子的有效性和稳定性,计算方法同IC均值加权法。如下:
① 进行因子相关性检验,以IR值作为分配权重的计算标准。如下图:



② 归一化处理,计算分配权重,如下:
动态市盈率权重 = 0.284/(0.284+0.268) = 0.5145
滚动市盈率权重 = 0.268/(0.284+0.268) = 0.4855


(二)根据得到的权重,重新合成大类因子

如下图,编写自定义因子。根据计算的权重值给各个因子赋予权重合成新的估值类因子。



合成因子后,在自定义因子页面点击【管理】,选入新合成的因子,再进入因子相关性界面,选择合成因子,重复上述分析操作进行检验,确定合成因子集。


第三步:选股打分 - 组建多因子策略

经过一系列的排名分析、重组合成,我们最终得到了有效的因子集合。最后,我们要根据这些因子的特性调试策略,构建稳定、持续的多因子选股策略。

如下图,将合成后的因子分别选入筛选条件和排名条件中,并按照各类因子的成分特征分配权重(方法同合成大类因子的权重确定),通过【每日选股】功能,对全市场股票进行筛选和排名,并计算排名分。



如上图,每日选股结果详细罗列了所有筛选条件和排名条件的数值,以及排名因子对应的排名分值大小,投资者可根据排名分靠前的股票所对应的因子数值,调整每日选股的筛选条件范围,不断的试错优化,最终找到稳定性、持续性和收益性兼备的多因子选股策略。


因子分析的操作步骤:



前言

  云端选股回测,是对全市场几千只股票池的定期轮动选股效果测试,用户可在因子分析中自定义多因子选股策略,通过云端选股回测动态筛选股票池并精准回测其收益效果。

  回测后提供收益曲线、收益分布、换手率分布等各种收益分析图表、信号/持仓明细及Beta、Alpha、信息比率、夏普比率等统计数据,供投资者多维度分析股票池的收益和抗风险能力,检验多因子选股策略的执行效果。

  对比基准收益,投资者可对动态股票池的超额收益能力及风险收益水平迅速做出判断,客观高效的评估因子策略的可行性,大大降低试错成本,提高策略生产效率。

优势

  • 多因子轮动选股
  • 提供全面统计数据和分析图表,量化分析选股策略和调仓策略
  • 云端极速运行,确保回测效率

案例

案例:云端选股回测,深度剖析选股有效性

一、云端秒速计算,轮动选股回测


云端选股回测通过回溯全市场证券的历史数据构建动态股票池,用户可自定义调仓周期,回测动态股票池的收益,这一计算量要远大于静态自选池的回测,为此,采用了最新的云端GPU高速运算处理机制,有效的保障了全市场扫描计算的吞吐量,极大的缩短回测时间,大大提高了策略的迭代效率。

如下图,对全市场3000多只股票近五年历史数据做轮动选股回测,仅用时6秒!



云端选股回测还提供交易池的轮动交易记录,记录交易池每天的持仓和权益变动。便于基金管理人向客户展示策略成果,根据资金分流程度,检验选股策略的稳定性和收益水平,如下图。



回测时充分结合各个时间截点的信号情况,包括证券派息、增股等信息,立体还原历史行情下的交易状态,保证回测和交易的一致性。如下图,是动态股票池的信号明细图,每一笔信号的时间、证券、价格、数量以及资金余额等信息都被悉数记录在内,帮助投资者分析因子对行情变化的敏锐度。



2、全面的收益统计报告


MQ云端选股回测后,提供全面的收益统计报告,并以收益数据统计和收益统计图表两种形式展示,帮助机构投资者量度及生产策略,多维度评估因子策略的效果,最终找到有效的投资组合。


收益统计数据:

(1)收益统计:

如下图,统计因子策略和基准指数的各项收益-风险指标并进行对比,包括总收益、年化收益、夏普比率、最大回撤率、收益波动率、信息比率、以及Beta和Alpha等,供投资者多维度分析动态股票池的收益水平和抗风险能力,理性评估因子策略的有效性,让量化投资触手可及。



(2)交易统计:

如下图,交易统计是对动态股票池整轮交易的综合统计,包括平均交易收益、正收益平均、负收益平均、交易赢率、月盈率、日赢率、指数跟踪误差、平均持仓股票数、平均持有天数、换股次数、持仓停牌股票比例、年换手率等指标,便于基金经理综合评估策略的稳定性。



收益统计图表:

(1)收益曲线:

如下图,将策略组合和基准指数的收益率绘制在一张图表上,策略是否跑赢大盘一目了然。
支持收益对比图和回撤对比图,投资者可结合相对收益图具体回溯历史每一天的相对收益,分析权益变动。基金经理不必进行复杂的建模计算,就可直接了解到动态股票池的超额收益水平,检验因子的赚钱能力。



(2)收益分布:

如下图,统计逐年、逐月和逐日的收益率根据时间轴横向展示收益变动,基金经理可以直观的了解因子策略在每个时间段的收益效果,分析因子的收益稳定性。



(3)权益曲线:

如下图,记录动态股票池的权益变动和资金占比情况,基金经理可结合夏普比率、信息比率等收益统计指标,综合度量股票池的盈利能力和抗风险水平,评估因子的稳定性和对资金流动性的敏感程度。



(4)最大回撤分布:

如下图,对比策略组合和基准指数的权益回撤,直观展现股票池对市场风险的敏感程度。投资者可结合自身风险承受能力理性评估因子的风险收益水平。



(5)换手率分布:

如下图,统计动态股票池在每个时间截面上的换手率大小,通过每一期买入股票的变动,评价因子的交易成本暴露,投资者可对比不同时段的换手率大小,评估因子策略的稳定性。



(6)年度收益统计:

如下图,按年度计算策略和基准指数的收益统计指标,对策略效果做纵向对比,便于投资者了解因子在每年度的表现效果,再结合各个时点的行情综合分析,优化因子策略。



(7)收益周期统计:

统计策略的绝对收益分布和相对指数收益分布,对比因子策略和基准指数的正收益和负收益次数分析因子是否具有超额收益的能力。如下图,因子策略的正收益次数大于基准指数的正收益次数,并且赢率高于基准策略,说明该因子组合具有一定的超额盈利能力。



操作步骤:

点击右上角【股票池】菜单 -> 【云端选股回测】,按下图①-③所示的操作步骤,进行云端动态选股回测。



前言

  云端定期轮动股票交易池的交易对象是全市场的几千只股票,是选股策略和定时调仓策略的综合应用。云端定期轮动股票交易池按照用户的多因子选股策略对全市场的几千只股票扫描计算,按照用户自定义的调仓周期定期轮动选股交易。

  实时记录每个股票成员的实际运行情况,并同步计算基准指数的收益率曲线。对比大盘收益,投资者可直观了解到动态股票池的盈利能力和超额收益水平。

  支持对整个交易池的持仓、资金进行管理,交易池中的模组共用一个资金池,一个模组平仓后释放的资金可用于其他模组,拒绝资金闲置,实现资金利用最大化。

优势

  • 实时统计组合个股运行明细
  • 组合收益实时对比基准指数收益
  • 全局控制资金账户,多个模组共用一份资金,资金利用率最大化
  • 多个组合策略同时运行

案例

案例一:定期轮动选股,智能交易

传统的买入持有策略势必会造成大量的资金闲置,不仅不利于做价值投资,而且一次性筛选出的静态交易池也很难跟随市场行情的变化,需要不断的筛选调整。云端股票交易池可对全市场证券定期轮动筛选,有效规避了人为调整的不确定性和静态交易池对市场的低适应性,实现了动态选股和轮动交易的一站式投资模式。

用户可根据投资思路自定义调仓周期,在每个调仓日,软件会按照因子策略在云端动态选股,并按照用户的交易设置选入股票池交易,同时剔除掉不满足选股条件的证券,如下图,充分保证交易池对市场的适应性和资金流动性。



案例二:云端股票池实现资金使用率最大化

机构投资者构建股票池时,不仅要兼顾股票池的收益性,更要考虑资金的流动性,避免闲置。云端定期轮动股票交易池可将一份资金分散投资到多只证券上,一篮子证券共用一份资金运行,在降低风险的同时也让资金得到了最大化利用。

如下图,是云端股票交易实时统计的组合中所有证券的运行明细,投资者可实时了解资金去向,对账户统筹管理。每个池子中的证券共用一份资金,证券平仓后释放的资金可用于其他证券开仓,实现资金的最大化利用。



案例三:云端股票池实时监控资金曲线

股票池的收益分析建立在大量的数据梳理和计算之上,投资者监控运行时很难实时计算统计。云端股票交易池提供可视化的收益统计效果,以图表的形式,实时对比因子策略和基准指数的收益率曲线、权益曲线,股票池的盈利能力和超额收益水平一目了然。


1、收益率曲线:

如下图,是云端定期轮动股票交易池实时统计的收益率曲线,对比大盘收益,投资者便可迅速了解股票池的超额收益水平,通过股票池对大盘涨跌的敏感程度评估因子策略的稳定性,及时优化交易系统。



2、权益曲线:

如下图,是云端定期轮动股票交易池的权益曲线,投资者可根据子账户权益变动和资金占比情况,结合股票池的运行明细具体分析收益效果和资金使用情况,合理分配资金统筹管理。



云端定期轮动股票交易池的工作流程:

第一步:调仓日当天使用因子分析中构建的多因子选股策略对全市场证券扫描计算,选择因子排名靠前的股票,并按照因子策略中最大持仓来计算交易股票的数量。

第二步:调仓日当天将满足因子选股条件的证券加入交易池交易,同时剔除交易池中不符合选股条件的证券。加入时会判断交易池的证券只数和占用的总资金,在可用资金足够并且证券只数小于256只的情况下才会有效加入。

第三步:以自定义的调仓周期为计算频率定期轮动计算,重复以上步骤。


操作步骤:

如下图①-④所示的操作步骤,点击右上角【股票池】菜单 -> 【云端定期轮动股票交易池】新建交易池进行轮动选股交易。



前言

  长期持有某股票做趋势交易,则意味着这部分资金将一直被占用,大大降低了机构资金的使用率和流动性。另外,根据现有的打新规则,必须根据资金配备一定市值的股票,这些股票往往长期持有,成为打新必备市值的同时,也成了不能流动的“无用钱”。

  若账户中有足够的持仓数量,可以实现盘中多次T+0交易,通过高点卖出—低点买入的反复操作盘活资金。软件支持T+0股票程序化,股票模型当日可以完成多次买卖,程序化交易模型把前一天的持仓卖掉当天再买回来,先卖后买一样可以日内盈利,变相实现了股票T+0交易。

优势

  • 在持有股票的情况下,股票程序化可以实现股票的T+0交易,盘活持仓,增加收益
  • 支持历史数据回测,回测支持计算T+0交易盈亏情况
  • 股票交易池各只股票共用一份资金,使资金使用率最大化
  • 支持资金管理等策略编写
  • 程序化交易利用计算机高速、快捷的处理能力,抓住股市上价格的瞬间变化,获得利润

案例

案例一:股票T+1运行模组 - 股票程序化交易

一、程序化波段交易增加交易机会,实现盈利创收

传统的股票交易模式是买入持有,这势必会导致大部分资金被长期占用,资金和持仓的流动性较差,相比波段交易缺乏更多的交易机会。股票程序化交易可以更加灵活的应用组合中各股票的价格波动,自动把握买卖机会,创造更多的利润。

我们针对自选池的股票编写了波段交易系统,并分别对波段交易系统和买入持有策略的收益效果进行对比进行,分析过程如下:


·策略思路:

利用MACD指标和BOLL通道两种技术分析指标,构建双指标的趋势交易系统


·源码如下:

Params
    Numeric P(2);
    Numeric M(9);
    Numeric Length(26);
    Numeric Offset(26);
    Numeric Length1(30);
    Numeric ShortLengh(12);
    Numeric LongLength(26);
Vars
    NumericSeries UpperMa;//计算30根K线最高价的EMA
    NumericSeries LowerMa;//计算30根K线最低价的EMA
    NumericSeries MidLine;//布林通道中轨
    NumericSeries TMP2;
    Numeric UpLine;//布林通道上轨
    Numeric DownLine;//布林通道下轨
    Numeric DIFF;
    Numeric DEA;
    Numeric CZ;
Setting
    AutoFinancing:True;//自动入金
    StockDivd:0;//设置股票前复权
Begin
    UpperMa=Ema(High,Length1);//计算30根K线最高价的EMA
    LowerMa=Ema(Low,Length1);//计算30根K线最低价的EMA
    MidLine=Ma(Close,Length);//布林通道中轨
    TMP2=Std(Close,Offset);
    UpLine=MidLine+P*TMP2;//布林通道上轨
    DownLine=MidLine-P*TMP2;//布林通道下轨
    DIFF = Ema(Close, ShortLengh) - Ema(Close, LongLength);//短周期与长周期的收盘价的指数平滑移动平均值做差。
    DEA = Ema(DIFF,M);//DIFF的M个周期指数平滑移动平均
    CZ = UpLine-DownLine;//定义布林通道的开口
    If(CZ>ref(CZ,1)&&DIFF>DEA) //布林通道开口放大,并且MACD红柱
    {
        Buy(100,Low);//买入
    }
    If(CROSS(CZ<ref(CZ,1),0.5)||CrossDown(Close,UPLINE)) //布林通道开口缩小,或者价格跌破布林上轨
    {
        Sell(Available_Opi,High);//卖出
    }
    If(CrossDown(Close,BKHigh-5)) //价格回撤4个点止损
    {
        Sell(Available_Opi,Close);//止损
    }
End


回测效果:

对自选池中11只股票进行组合回测,在2012年-2018年中,买入持有的资金曲线(下图1)和程序化波段策略的资金曲线(下图2):


(图1)



(图2)


·收益统计:


买入持有策略 程序化波段交易
盈利率 资金占用天数占比 盈利率 资金占用天数占比
丰华股份 35.67% 100.00% 1256.22% 41%
汉得信息 189.77% 100.00% 600.73% 42%
宏达矿业 -40.39% 100.00% 696.31% 38%
贤丰控股 69.44% 100.00% 284.99% 42%
上海三毛 8.09% 100.00% 1177.83% 39%
江苏索普 9.1% 100.00% 736.24% 44%
当代东方 68.96% 100.00% 889.68% 32%
宜华健康 482.29% 100.00% 250.51% 46%
洪都航空 -48.02% 100.00% 923.11% 39%
荣华实业 -52.44% 100.00% 391.46% 46%
融捷股份 38.60% 100.00% 503.13% 35%
一篮子股票组合 761.07%   7710.21%  

可见,程序化波段交易策略在6年的时间内实现了更多的交易机会,并通过自动的波段趋势交易让资金和持仓流动起来,创造了翻倍的盈利收益。


二、股票全自动运行模组,实现资金和持仓的独立管理

股票全自动运行模组中各模组独立运行,互不干扰,给每一个合约-周期-策略构成的交易单元分配一定的资金,根据策略信号自动交易并统计各自的盈亏和持仓,完全的独立运行管理。为保证资金的合理运用,编写时可以在模型中写入AutoFinancing函数,可实现各个模组按需自动入金。


如下图,是股票T+1运行模组的运行页面,可以查看到各模组的独立运行明细。



案例二:股票T+0交易池 - 盘活持仓,创造附加利润

一、股票T+0策略,盘活持仓实现利润最大化

T+1交易制度下,当天买入的股票只能在下一交易日卖出,具有隔夜风险。尤其是机构用户,长期持有大量的股票,无异于将持仓完全暴露于市场的风险中!但如果合理利用现行规则,采取适当的操作方式也可以实现股票T+0交易,利用底仓在日内先卖后买,变相的实现T+0交易,盘活持仓,达到利润最大化。


·策略思路

T+0程序化交易本质上是利用底仓,在日内震荡到高点时抛售,当价格回落到低点时再买回的过程。

高点抛售思路:以开盘跳空、较昨收盘上涨5%、多头排列状态下当日振幅大于6%的K线作为冲高、跳空的卖出形态,在高点上抛售;

低点买回思路:以最低点比最近一次卖点低80点位以下或者连续3根K线呈空头排列,作为回调的买入形态,在低点买回。


·源码如下

Vars
    Numeric MA1;//5周期均线
    Numeric MA2;//10周期均线
    Numeric MA3;//20周期均线
    Numeric ZF;//振幅
    Numeric DT;//多头变量
    Numeric KT;//空头变量
    Numeric TK;//跳空形态
    Numeric SZ;//上涨形态

Setting
    StockT0_Plus:True;//设置股票T+0交易
    AutoFinancing:True;//启用按需自动入金

Begin
    MA1 = Ma(Close,5);//5周期均线
    MA2 = Ma(Close,10);//10周期均线
    MA3 = Ma(Close,20);//20周期均线
    ZF = (High-low)/low;//振幅
    DT = MA1>MA2&&MA2>MA3&&MA1>Ref(MA1,1)&&MA2>Ref(MA2,1)&&MA3>Ref(MA3,1);
    //均线多头排列定义为多头变量
    KT = MA1<MA2&&MA2<MA3&&MA1<Ref(MA1,1)&&MA2<Ref(MA2,1)&&MA3<Ref(MA3,1);
    //均线空头排列定义为空头变量
    TK = DayBarPos == 1&& Open>ref(Close,1)*1.02;//开盘跳空形态
    SZ = (High - Ref(Close,DayBarPos-1))/Ref(Close,DayBarPos-1);
    //较昨日收盘的上涨幅度
    //开盘跳空、较昨收盘上涨5%或多头排列状态下当日振幅大于6%的K线视为冲高、跳空的卖出形态
    if(TK || SZ>0.05 || ZF >0.06&&DT )
    SellShort(LimitVol,High); //最低点比最近一次卖出点位低80点位以下或者连续3根K线呈空头排列,视为回调的买入形态
    if(Low<SKPrice-80*MinPrice||Every(KT,3))
    BuyToCover(SKVol,Low);
End
注:
编写股票T+0程序化策略时,需要在模型中写入 StockT0_Plus函数,设置股票T+0交易。


T+0实现利润最大化情形1:

持续盈利行情下,采用T+0交易策略,在行情冲高时抛售底仓,在价格回落时再低价买回,相比长期持有策略能够更大程度的锁住利润。



T+0实现利润最大化情形2:

跳空高开行情下,采用T+0策略在开盘大幅跳空时抛售底仓,待价格回落时买回,先卖后买操作后,持仓成本未变,还赚取了行情极速拉升带来的超额收益。



T+0实现利润最大化情形3:

盘整行情下,一直持有可能会不赔不赚,但如果采用T+0策略在波段高点卖出,低点买回,却可以不断的从震荡中获取收益,盘活持仓,流动获利。



测试效果:

股票T+0程序化策略支持历史数据回测,并统计交易的盈亏和自选股的整体收益效果。如下图,是股票T+0程序化策略的组合回测效果。




二、股票T+0交易池,实现资金最大化利用

现有的打新规则要求必须根据资金配备一定市值的股票,这就意味着机构用户将囤积大量的持仓,资金和股票的流动性被大大拉低。股票T+0交易池,可以多只股票共用一份资金,配合T+0的高抛低吸轮动交易,让资金和持仓充分利用,盘活底仓实现盈利增值!


如下图,是股票T+0交易池的运行界面,交易池中的股票共用一份资金,个股卖出释放的资金,可以用于买回其他的股票,让闲置资金和底仓流动起来。



前言

  利用传统的投资工具,投资者只能通过判断市场的涨跌获取收益,而利用期权,无论是趋势市还是震荡市,几乎在所有的市场预期下,投资者都有相应的策略来捕获盈利并控制风险。期权的非线性损益结构使得期权在风险管理、组合投资方面具有了明显的优势。可以通过不同期权及其他非线性投资工具的组合,构造不同风险收益状况的投资组合。但由于期权合约众多,交易时又需要考虑多重因素的影响并进行大量的计算和分析,因此人工交易势必会因为无法完成大量计算分析而错失交易机会的问题。

  具有专业的期权交易平台,提供专业的T型报价、损益平衡图。同时能够利用计算机语言将期权定价模型策略、期权市场波动套利策略等复杂交易思路转换成可以自动计算并执行策略的全自动程序化交易系统,捕获期权市场各种转瞬即逝的机会。

优势

  • T型报价
  • 损益平衡图
  • 支持期权定价模型、期权市场波动套利等策略的执行和编写

案例

案例一:期权定价模型

1、什么是期权定价公式

Black-Scholes-Merton期权定价模型(Black-Scholes-Merton Option Pricing Model),即布莱克—斯克尔斯期权定价模型。

B-S-M定价公式:C=S·N(d1)-X·exp(-r·T)·N(d2)

其中:

d1=[ln(S/X)+(r+σ^2/2)T]/(σ√T) d2=d1-σ·√T

C—期权初始合理价格 X—期权执行价格

S—所交易金融资产现价T—期权有效期

r—连续复利计无风险利率

σ—股票连续复利(对数)回报率的年度波动率(标准差)

N(d1),N(d2)—正态分布变量的累积概率分布函数,在此应当说明两点:

第一,该模型中无风险利率必须是连续复利形式。一个简单的或不连续的无风险利率(设为r0)一般是一年计息一次,而r要求为连续复利利率。r0必须转化为r方能代入上式计算。

两者换算关系为:r=LN(1+r0)或r0=exp(r)-1例如r0=0.06,则r=LN(1+0.06)=0.0583,即100以583%的连续复利投资第二年将获106,该结果与直接用r0=0.06计算的答案一致。

第二,期权有效期T的相对数表示,即期权有效天数与一年365天的比值。如果期权有效期为100天,则T=100/365=0.274。


2、年化波动率及BS公式函数计算

①计算年化波动率:

记录收盘价:CallOptions.PutOptions

计算对数Ln(今日收盘价/昨日收盘价)

计算N天的标准差:如N天标准差=STD()

计算N天的波动率:N天的标准差*SQRT(252)

计算年化的波动率:N天波动率/SQRT(2N)

②推导理论价格计算案例

例如:某股票市价为3.84元,无风险利率为6%,年波动率为15%,求工商银行行权价为3.6元、期限为半年的欧式认购期权和认沽期权价格,其中:期限内不支付红利。

此例中S=3.84,K=3.6,r=0.06,σ=0.15,T=0.5。

计算过程可分为三步:

第一步,先计算出和。






第二步,计算和。由标准正态分布表可查的



则可得



第三步,将上述结果及已知条件代入B-S公式,股票欧式认购期权价格为:



欧式认沽期权价格为:




3、B-S-M模型失效或者可能误差的原因:

①模型对平值期权的估价令人满意,特别是对剩余有效期限超过两月,且不支付红利者效果更好一点。

②对于高度增值或减值的期权,模型的估价有较大偏差,会高估减值期权而低估增值期权。

③对临近到期日的期权的估价存在一定的误差。

④离散度过高或过低的情况下,会低估低离散度的买入期权,高估高离散度的买方期权。

⑤模型基于对市场部分情况的假设条件过于严苛,这与现实情况有所差别,可能会影响到模型的可靠性。


4、根据以上推导过程,编写策略模型计算期权理论价格,形成套利策略如下:

Data
     datac:"50ETF1707-C-2.6000"; //看涨期权
     datap:"50ETF1707-P-2.600"; //看跌期权
     dataf:"50ETF"; //标的合约
Params
     Numeric R(1); //无风险收益率
     Numeric N(5);                          
Vars
     Numeric HistoryVolatility_C; //看涨期权的历史波动率
     Numeric HistoryVolatility_P; //看跌期权的历史波动率
     Numeric HistoryVolatility_F; //标的合约的价格波动率
     Numeric New_C; //看涨期权的最新价
     Numeric New_P; //看跌期权的最新价
     Numeric New_F; //标的合约的最新价
     Numeric ExercisePrice; //行权价
     Numeric TheoreticalPrice_C; //看涨期权的理论价格
     Numeric TheoreticalPrice_P; //看跌期权的理论价格
     Numeric RTS; //距行权日剩余的天数
     Numeric D1; //中间值
     Numeric D2; //中间值
     Numeric BuyRemainPosition_C; //看涨期权多头可用持仓
     Numeric BuyRemainPosition_P; //看跌期权多头可用持仓
     Numeric BuyRemainPosition_F; //标的合约多头可用持仓
     Numeric SellRemainPosition_F; //标的合约空头可用持仓                 
Begin
     HistoryVolatility_C = datac.Price("HistoricalVolatility");//取看涨期权的历史波动率
     HistoryVolatility_P = dataf.Price("HistoricalVolatility");//取看跌期权的历史波动率


     New_F = dataf.Price("New");//取50ETF合约的最新价
     ExercisePrice = datac.Price("StrikePrice");//取行权价


     RTS = DateDiff(Date,datac.Price("ExpirationDate"));//距行权日到期剩余天数
     HistoryVolatility_F = Std(Ln(Close/Close[1]), N) * Sqrt(252) / Sqrt(2 * N);//计算标的合约的价格波动率


     D1 = (Ln(New_F / ExercisePrice) + (R/10 + 0.5 * Power(HistoryVolatility_F,2)) * RTS) / (HistoryVolatility_F * Power(RTS,0.5));//求期权理论价格的中间值
     D2 = D1 - HistoryVolatility_F * Power(RTS,0.5);//求期权理论价格的中间值


     TheoreticalPrice_C = New_F * NormDist(D1) - ExercisePrice * Exp(-1 * (R / 10) * RTS) * NormDist(D2);//根据B-S-M公式计算看涨期权理论价格
     TheoreticalPrice_P = ExercisePrice * Exp(-1 * (R / 10) * RTS) * (1 - NormDist(D2)) - New_F * (1 - NormDist(D1));//根据B-S-M公式计算看跌期权理论价格


     New_C = datac.Price("New");//看涨期权的最新价
     New_P = dataf.Price("New");//看跌期权的最新价


     BuyRemainPosition_C = datac.F_BuyRemainPosition();//看涨期权多头可用持仓
     BuyRemainPosition_P = datap.F_BuyRemainPosition();//看跌期权多头可用持仓
     BuyRemainPosition_F = dataf.F_BuyRemainPosition();//标的合约多头可用持仓
     SellRemainPosition_F = dataf.F_SellRemainPosition();//标的合约空头可用持仓


     If((TimeDiff(Date,datac.Price("ExpirationDate")) <= 5 || TimeDiff(Date,datap.Price("ExpirationDate")) <= 5) && (GetGlobalVar(0) == 1 || GetGlobalVar(1) == 1))
    //看涨期权或看跌期权行权日前五天
     {
             If(BuyRemainPosition_C > 0)//看涨期权有多头可用持仓
             {
                    datac.A_SendOrder(Enum_sell,Enum_Exit,BuyRemainPosition_C,datac.Price("Bid"));//对价卖平看涨期权可用多仓
             }

            If(BuyRemainPosition_P > 0)//看跌期权有多头可用持仓
             {
                    datap.A_SendOrder(Enum_Sell,Enum_Exit,BuyRemainPosition_P,datap.Price("Bid"));//对价卖平看跌期权可用多仓
             }

            If(BuyRemainPosition_F > 0)//标的合约有空头可用持仓
             {
                    dataf.A_SendOrder(Enum_Buy,Enum_Exit,BuyRemainPosition_F,dataf.Price("Ask"));//对价买平标的合约可用空仓
             }

            If(SellRemainPosition_F > 0)//标的合约有多头可用持仓
             {
                    dataf.A_SendOrder(Enum_Sell,Enum_Exit,SellRemainPosition_F,dataf.Price("Bid"));//对价卖平标的合约可用多仓
             }

            SetGlobalVar(0,0);
             SetGlobalVar(1,0);
     }

    Else
    {
       If(New_C < TheoreticalPrice_C * 0.5 && datac.Price("Stdderiation") < HistoryVolatility_C && BuyRemainPosition_C == 0 && SellRemainPosition_F == 0 && GetGlobalVar(0) == 0)
       //看涨期权的最新价低于看涨期权理论价格的50%,并且看涨期权的隐含波动率低于历史波动率,并且看涨期权没有多头持仓,对应标的合约没有空头持仓
       {
               datac.A_SendOrder(Enum_Buy,Enum_Entry,1,datac.Price("Ask"));//对价买开看涨期权1手
               dataf.A_SendOrder(Enum_Sell,Enum_Entry,1,dataf.Price("Bid"));//对价卖开标的合约1手
               SetGlobalVar(0,1);
       }

      If(TheoreticalPrice_P > New_P * 0.5 && datap.Price("Stdderiation") > HistoryVolatility_P && BuyRemainPosition_P == 0 && BuyRemainPosition_F == 0 && GetGlobalVar(1) == 0)
       //看跌期权理论价格高于看跌期权最新价的50%,并且看跌期权隐含波动率高于历史波动率,并且看跌期权没有多头持仓,对应标的合约没有多头持仓
       {
               datap.A_SendOrder(Enum_Buy,Enum_Entry,1,datap.Price("Ask"));//对价买开看跌期权1手
               dataf.A_SendOrder(Enum_buy,Enum_Entry,1,dataf.Price("Ask"));//对价买开标的合约1手
               SetGlobalVar(1,1);
       }
    }

End


5、策略运行

在日线上编写策略模型,计算历史波动率,装入期权运行页面。策略模型直接读取相应期权认沽认购两合约的历史数波动率进行套利计算。



案例二:期权市场波动套利

1、买卖权平价套利

①什么是买卖权平价关系?

买卖权平价关系是指具有相同的到期日与执行价格的金融工具,其卖权与买权价格间必然存在的基本关系。如果两者不相同,则存在套利的空间。

欧式期权的平价关系可以表示为:

其中,C为看涨期权的当期的理论价格,P为看跌期权当期的理论价格,1年期无风险利率为r,行权价为K,为某股票现在的价格。


②根据买卖权平价关系推导套利条件

在上述理论的基础上,Tucker根据股指期货和股指期权之间的价格关系阐述了期货与期权的平价关系,并运用这一均衡关系来发现市场的套利机会并验证市场效率。

该理论假设:1)期权为欧式期权。2)税收、手续费等交易成本不计。3)借贷利率相等。

在t时刻,期货和期权的平价关系可表示为:

F为t时刻期货价格,T为期权到期日,C、P分别是看涨看跌期权的价格,r为无风险利率,贴现因子以年化无风险利率计算,通常考虑一个月内(1/12),其数值接近于1,进而上述关系可简化为:

由上式可以推导出套利开仓条件:


策略
C-P>F-K 看涨期权价格被高估,看跌期权价格被低估 卖出看涨期权,买入看跌期权
C-P<F-K 看涨期权价格被低估,看跌期权价格被高估 买入看涨期权,卖出看跌期权

③将上述策略编写为策略模型

Data
  datac:"m1709-C-2850"; //定义看涨期权
  datap:"m1709-P-2850"; //定义看跌期权
  dataf:"m1709"; //定义期权标的物
Vars
  Numeric New_C; //定义看涨期权最新价
  Numeric New_P; //定义看跌期权最新价
  Numeric New_F; //定义标的物最新价
  Numeric OptionStrikePrice; //定义行权价
  Numeric BuyRemainPosition_C; //定义看涨期权多头持仓
  Numeric SellRemainPosition_C; //定义看涨期权空头持仓
  Numeric BuyRemainPosition_P; //定义看跌期权多头持仓
  Numeric SellRemainPosition_P; //定义看跌期权空头持仓
  Global_NumericArray SKID_C[5000]; //卖看涨期权索引数组
  Global_NumericArray BKID_C[5000]; //买看涨期权索引数组
  Global_NumericArray SKID_P[5000]; //卖看跌期权索引数组
  Global_NumericArray BKID_P[5000]; //买看跌期权索引数组
  Global_NumericArray Time1[5000]; //卖看涨期权,买看跌期权时间数组
  Global_NumericArray Time2[5000]; //买看涨期权,卖看跌期权时间数组
  Global_Numeric i; //卖看涨期权,买看跌期权次数
  Global_Numeric j; //买看涨期权,卖看跌期权次数
  Global_Numeric Index; //定义全局变量用于撤单
  Global_Numeric Coin; //定义全局变量控制卖看涨期权,买看跌期权
  Global_Numeric Coin1; //定义全局变量控制买看涨期权,卖看跌期权
  Numeric NumOrder; // 是否下单标志 0未下单 1下单
Begin
  if(RunStart==1)
  {
   If(i >= 5000 || j >= 5000)//卖看涨期权,买看跌期权次数或者买看涨期权,卖看跌期权次数>=5000
  {
  Commentary("记录次数耗尽,请重新加载此模型");//输出记录次数耗尽,请重新加载此模型
  }

  Else
  {
  New_C = datac.Price("New");//看涨期权最新价
  New_P = datap.Price("New");//看跌期权最新价
  New_F = dataf.Price("New");//标的物最新价
  OptionStrikePrice = datac.Price("StrikePrice");//行权价
             BuyRemainPosition_C=datac.F_BuyRemainPosition();//看涨期权多头持仓
             SellRemainPosition_C=datac.F_SellRemainPosition();//看涨期权空头持仓
             BuyRemainPosition_P=datap.F_BuyRemainPosition();//看跌期权多头持仓
             SellRemainPosition_P=datap.F_SellRemainPosition();//看跌期权空头持仓
     If(DateDiff(CurrentDate,datac.Price("ExpirationDate")) <= 5 || DateDiff(CurrentDate,datap.Price("ExpirationDate")) <= 5)//看涨或者看跌期权距离行权日5日内
     {
  If(BuyRemainPosition_C > 0)//看涨期权多头持仓>0
  {
  datac.A_SendOrder(Enum_Sell,Enum_Exit,BuyRemainPosition_C,datac.Price( "Bid1"));//以买1价卖平看涨期权
  }

  If(BuyRemainPosition_P > 0)//看涨期权空头持仓>0
  {
  datac.A_SendOrder(Enum_Buy,Enum_Exit,BuyRemainPosition_P,datac.Price("Ask1"));//以卖一价买平看涨期权空头持仓
  }

  If(BuyRemainPosition_P > 0)//看跌期权多头持仓>0
  {
  datap.A_SendOrder(Enum_Sell,Enum_Exit,BuyRemainPosition_P,datap.Price( "Bid1"));//以买一价卖平看跌期权多头持仓
  }  

  If(SellRemainPosition_P > 0)//看跌期权空头持仓>0
  {
  datap.A_SendOrder(Enum_Buy,Enum_Exit,SellRemainPosition_P,datap.Price( "Ask1"));//以卖一价买平看跌期权
  }  
  }

 Else If(New_C - New_P > New_F - OptionStrikePrice)//看涨期权最新价-看跌期权最新价>标的物最新价-行权价
  {

 If(BuyRemainPosition_C > 0)//看涨期权多头持仓>0
 {
 datac.A_SendOrder(Enum_sell,Enum_Exit,BuyRemainPosition_C,datac.Price( "Bid1"));//以看涨期权买一价卖平看涨期权多头持仓
 Coin = 0;//Coin为0
 }
 If(SellRemainPosition_P > 0) //看跌期权空头持仓>0
 {
 datap.A_SendOrder(Enum_Buy,Enum_Exit,SellRemainPosition_P,datap.Price( "Ask1"));//以看跌期权卖一价买平看跌期权空头持仓
 Coin = 0;//Coin为0
 }  
 If(BuyRemainPosition_C == 0 && SellRemainPosition_C == 0 && Coin == 0) //如果看涨期权多头持仓0并且看跌期权空头持仓0并且Coin为0
 {
 SKID_C[i] = datac.A_SendOrder( Enum_Sell,Enum_Entry,1,datac.Price("Bid1"));//以看涨期权买一价卖开看涨期权1手的卖开索引
 BKID_P[i] = datap.A_SendOrder( Enum_Buy,Enum_Entry,1,datap.Price( "Ask1"));//以看跌期权卖一价买开看跌期权1手的买开索引
 Time1[i] = CurrentTime;//第i个索引的时间
 Index = 0;//index为0
 Coin = 1;//coin为1
 NumOrder = 1;//下单后1
                           }
          While(Index < i)
 {
 If(F_OrderStatus(SKID_C[Index]) == Enum_Declared && TimeDiff(Time1[Index],CurrentTime) >= 15)//15秒没有成交
 {
datac.A_DeleteOrder(F_OrderContractNo(SKID_C[Index]));//撤单
datac.A_SendOrder( Enum_Sell,Enum_Entry,1,datac.Price("FallLimit"));//以跌停价卖开1手看涨期权          
 }

 If(F_OrderStatus(BKID_P[Index]) == Enum_Declared && TimeDiff(Time1[Index],CurrentTime) >= 15)//15秒没有成交
 {
datap.A_DeleteOrder(F_OrderContractNo(BKID_P[Index]));//撤单
datap.A_SendOrder( Enum_Buy,Enum_Entry,1,datap.Price("RiseLimit"));//以涨停价买开1手看跌期权
 }
                                    Index = Index + 1;
      }      
                            If (NumOrder == 1)//每次下单后
 {
 i = i + 1;//次数加1
 NumOrder = 0;//清0
  }
     }
 Else If(New_C - New_P < New_F - OptionStrikePrice)//看涨期权最新价-看跌期权最新价<标的物最新价-行权价
 {
 If(SellRemainPosition_C > 0)//看涨期权空头持仓>0
                            {
 datac.A_SendOrder( Enum_buy,Enum_Exit,SellRemainPosition_C,datac.Price( "Ask1")); //以卖一价买平看涨期权空头持仓
 Coin1=0;//Coin1为0
 }
 If(BuyRemainPosition_P > 0) //看跌期权多头持仓>0
 {
 datap.A_SendOrder(Enum_Sell,Enum_Exit,BuyRemainPosition_P,datap.Price("Bid1"));//以买一价卖平看跌期权多头持仓
 Coin1 = 0;//Coin1为0
 }  
 If(SellRemainPosition_C == 0 && BuyRemainPosition_P == 0 && Coin1 == 0) //看涨期权空头持仓为0并且看跌期权多头持仓为0且Coin1为0
 {
 BKID_C[j] = datac.A_SendOrder( Enum_Buy, Enum_entry, 1, datac.Price("Ask1"));//以卖一价买开看涨期权1手的索引
 SKID_P[j] = datap.A_SendOrder( Enum_Sell, Enum_Entry, 1, datap.Price( "Bid1"));//以买一价卖开看跌期权1手的索引
 Time2[j] = CurrentTime;//第i个索引的时间
  Index = 0;//Index为0
  Coin1 = 1;//Coin1为1
  NumOrder = 1;//下单后1
                            }
 While(Index < j)
 {
If(F_OrderStatus(BKID_C[Index]) == Enum_Declared && TimeDiff(Time2[index],CurrentTime) >= 15)// 15秒没有成交
{
datac.A_DeleteOrder(F_OrderContractNo(BKID_C[Index]));//撤单
datac.A_SendOrder(Enum_Buy,Enum_Entry,1,datac.price("RiseLimit"));//以涨停价买开1手看涨期权
}
If(F_OrderStatus(SKID_P[Index]) == Enum_Declared && TimeDiff(Time2[j],CurrentTime) >= 15)// 15秒没有成交
{
datap.A_DeleteOrder(F_OrderContractNo(SKID_P[Index]));//撤单
datap.A_SendOrder( Enum_Sell,Enum_Entry,1,datap.Price("FallLimit"));//以跌停价卖开1手看跌期权
}
                                       Index = Index + 1;
 }
 If (NumOrder == 1)//每次下单后
 {
j = j + 1;//下单次数+1
   NumOrder = 0;//清0
}
}
     }
}
End


④策略运行:

将策略模型在期权K线主图进行计算后加载到期权运行页面,满足编写的套利条件后,会直接进行套利委托下单。



2、日历价差套利

①日历价差套利策略简介

日历价差是指买进到期日较远的期权,同时又卖出相同行权价格、相同数量但到期日较近的期权,赚取两个不同期权隐含波动率的差价或者其它期权定价参数的差价,以获得利润的期权套利交易策略。


②推导套利条件

1)、开仓策略:

X=V(近月隐含波动率)-V (历史波动率)+V (近月隐含波动率 )-V(远月隐含波动率) X>0,通过卖出一份近月看涨期权,同时买出一份远月看涨期权,建立日历价差组合。

2)、平仓策略:

考虑到该组合并没有对冲标的物价格变动的风险,在近月期权到期前几天,组合的Gamma风险值可能比较大,标的物价格的变化会大幅增加收益的波动率。因此,策略采取在当月合约到期前8天进行平仓。


③将上述策略编写为策略模型

Data
       Data1:"m1709-C-2850";                           //定义近月合约
       Data2:"m1801-C-2850";                      //定义远月合约
Params
     Numeric Lots(2); //定义参数,下单手数,默认值为2
     Numeric Length(5); //定义参数,周期,默认值为5
Vars
     Numeric HistoryVolatility; //定义变量,取历史波动率
     Numeric Stdderiation1; //定义变量,取近月合约的隐含波动率
     Numeric Stdderiation2; //定义变量,取远月合约的隐含波动率
     Numeric Cond; //定义变量,判断入场条件
     Numeric SellRemainPosition; //定义变量,取空头可用持仓
     Numeric BuyRemainPosition; //定义变量,取多头可用持仓
Begin
     HistoryVolatility = Std(Ln(Close / Close[1]), Length) * Sqrt(252);//计算标的物历史波动率
     Stdderiation1 = data1.Price("Stdderiation");//取近月合约的隐含波动率
     Stdderiation2 = data2.Price("Stdderiation");//取远月合约的隐含波动率
     Cond = Stdderiation1 - HistoryVolatility + Stdderiation1 - Stdderiation2;//计算隐含波动率的综合价差

If(Cond > 0 && GetGlobalVar(0) == 0)//当价差大于0并且没有持仓时建立日历价差组合
{
        data1.A_SendOrder(Enum_Sell,Enum_Entry,Lots,data1.Price("Ask1"));//卖出近月期权合约
        data2.A_SendOrder(Enum_Buy,Enum_Entry,Lots,data2.Price("Bid1")); //买入远月期权合约
        SetGlobalVar(0,1);
}

If(GetGlobalVar(0) == 1 && DateDiff(Date,data1.Price("ExpirationDate")) <= 5)//当距行权日期小于等于5天时平仓
{
        BuyRemainPosition = data2.F_BuyRemainPosition();//取远月合约的多头可用持仓

       If(BuyRemainPosition > 0)//如果远月合约的多头可用持仓大于0
        {
                data2.A_SendOrder(Enum_Sell,Enum_Exit,BuyRemainPosition,data1.Price("Bid1")); //平多头可用持仓
        }

       SellRemainPosition= data1.F_SellRemainPosition();//取近月合约的空头可用持仓

       If(SellRemainPosition > 0)//如果近月合约的空头可用持仓大于0
        {
                data1.A_SendOrder(Enum_Buy,Enum_Exit,SellRemainPosition,data1.Price("Ask1")); //平空头可用持仓
         }

       SetGlobalVar(0,0);
}

End


④策略运行:

日历价差套利策略需要计算近月合约的历史波动率,在日线上编写策略模型,计算历史波动率,装入期权运行页面。策略模型直接读取相应期权认沽认购两合约的历史数波动率进行套利计算。



3、牛市看涨期权垂直套利

垂直套利也称货币套利、跨价套利。采用这种套利方式,可以将风险和收益限定在一定范围内。它的交易方式表现为按照不同的履约价格同时买进和卖出同一到期月份的看涨期权或看跌期权。之所以称为“垂直套利”,是因为垂直套利策略除履约价格外,其余都是相同的,而履约价格及其反映的权利金在期权行情表上是垂直排列的。

牛市看涨期权垂直套利是风险和收益都有限的策略。该策略可用作“摸底”之用,投资者预期市价的下跌趋势已经接近尾声,随时可作反弹。无论投资者预期正确与否,组合投资者一样站在较为有利的位置。该策略的损益情况如下图所示:



①标的合约价格分析

编写策略模型,寻找在下跌趋势尾声,即将反弹的价格点,作为套利入场的时机。



②选择期权合约,形成套利策略

在选择期权合约时需要考虑最大风险与回报。如果要风险最低,则选择的买入的履约价格与卖出的履约价格相距越窄越好,因为权利金最少。如果要实现最大回报越高越好,由于最大回报的计算是两个履约价格之差减去净权利金,则买入和卖出的两个履约价格越宽越好。根据自己对风险与收益的不同偏好,选择不同的期权合约,形成套利策略。

Data
  data1:"m1709-C-2800"; //定义第一个看涨期权合约
  data2:"m1709-C-2850"; //定义第二个看涨期权合约
Params
  Numeric Lots(2); //定义手数
  Numeric Length1(5); //定义周期数
  Numeric Length2(10); //定义周期数
Vars
  Numeric MA1; //均线
  Numeric MA2; //均线
  Numeric OptionStrikePrice1; //行权价
  Numeric OptionStrikePrice2; //行权价
  Numeric BKfun; //BK条件
  Numeric SPfun; //SP条件
Begin
  MA1 = Ma(Close,Length1);   //5周期均线
  MA2 = Ma(Close,Length2);   //10周期均线

  BKfun = CrossUp(MA1,MA2);//5周期均线上穿10周期均线
  SPfun = CrossDown(MA1,MA2);//5周期均线下穿10周期均线

  OptionStrikePrice1 = data1.Price("StrikePrice");//取data1合约的行权价
  OptionStrikePrice2 = data2.Price("StrikePrice");//取data2合约的行权价     
                                                              
  If(BKfun && OptionStrikePrice2 > OptionStrikePrice1 && GetGlobalVar(0) == 0)//满足BK信号条件并且合约2的行权价>合约1的行权价
  {
  data1.A_SendOrder(Enum_Buy,Enum_Entry,Lots,data1.Price("Ask1"));//对合约1以卖价1 的价格发出买2手开仓委托
  data2.A_SendOrder(Enum_Sell,Enum_Entry,Lots,data2.Price("Bid1"));//对合约2以买价1 的价格发出卖2手开仓委托
  SetGlobalVar(0,1);//全局变量为1
  }

  If(SPfun && data1.F_BuyRemainPosition() > 0 && data2.F_SellRemainPosition() > 0&&GetGlobalVar(0) == 1)//满足平仓信号并且合约1有多头持仓并且合约2有空头持仓
  {
  data1.A_SendOrder(Enum_Sell,Enum_Exit,data1.F_BuyRemainPosition(),data1.Price("Bid1"));//对合约1以买价1 的价格发出全卖平的委托
  data2.A_SendOrder(Enum_Buy,Enum_Exit,data2.F_SellRemainPosition(),data2.Price("Ask1"));//对合约2以卖价1 的价格发出全买平的委托
  SetGlobalVar(0,0);//全局变量为0
  }

End


③策略运行

将策略模型在K线主图进行计算后加载到运行模组中,用来判断标的物的价格趋势,寻找套利的入场时机。加载成功后,对应的策略模型会自动运行,无需手动操作,满足条件后,自动对期权合约进行套利下单。


4、牛市看跌期权垂直套利

牛市看跌期权垂直套利是看好后市的保守投资策略。投资者看好后市,希望卖出看跌期权赚取权利金,但是又担心市价跌破履约价格会带来风险,因此,用收取的部分权利金买入一个较低履约价格的看跌期权以控制风险。该策略的损益情况如下图所示:



①标的合约价格分析

编写策略模型,判断市场氛围,在价格上涨趋势中,寻找套利入场时机。



②选择期权合约,形成套利策略

根据自己对风险与收益的不同偏好,选择不同的期权合约,形成套利策略

Data
  data1:"m1701-P-2800"; //定义第一个看跌期权合约
  data2:"m1701-P-2850"; //定义第二个看跌期权合约
Params
  Numeric Lots(2); //定义手数
  Numeric Length1(5); //定义周期数
  Numeric Length2(10); //定义周期数
Vars
  Numeric MA1; //均线
  Numeric MA2; //均线
  Numeric OptionStrikePrice1; //行权价
  Numeric OptionStrikePrice2; //行权价
  Numeric BKfun; //BK条件
  Numeric SPfun; //SP条件
Begin
  MA1 = Ma(Close,Length1);    //5周期均线
  MA2 = Ma(Close,Length2);    //10周期均线

  BKfun = CrossUp(MA1,MA2);//5周期均线上穿10周期均线
  SPfun = CrossDown(MA1,MA2);//5周期均线下穿10周期均线

  OptionStrikePrice1 = data1.Price("StrikePrice");//取data1合约的行权价
  OptionStrikePrice2 = data2.Price("StrikePrice");//取data2合约的行权价

  If(BKfun && OptionStrikePrice2 > OptionStrikePrice1 && GetGlobalVar(0) == 0)//满足BK信号条件并且合约2的行权价>合约1的行权价
  {
  data1.A_SendOrder(Enum_Buy,Enum_Entry,Lots,data1.Price("Ask1"));//对合约1以卖价1 的价格发出买2手开仓委托
  data2.A_SendOrder(Enum_Sell,Enum_Entry,Lots,data2.Price("Bid1"));//对合约2以买价1 的价格发出卖2手开仓委托
  SetGlobalVar(0,1);//全局变量为1
  }

  If(SPfun && data1.F_BuyRemainPosition() > 0 && data2.F_SellRemainPosition() > 0&&GetGlobalVar(0) == 1)//满足SP信号并且合约1有多头持仓并且合约2有空头持仓
  {
  data1.A_SendOrder(Enum_Sell,Enum_Exit,data1.F_BuyRemainPosition(),data1.Price("Bid1"));//对合约1以买价1 的价格发出全卖平的委托
  data2.A_SendOrder(Enum_Buy,Enum_Exit,data2.F_SellRemainPosition(),data2.Price("Ask1"));//对合约2以卖价1 的价格发出全买平的委托
  SetGlobalVar(0,0);//全局变量为0

  }

End


③策略运行

将策略模型在K线主图进行计算后加载到运行模组中,用来判断标的物的价格趋势,寻找套利的入场时机。加载成功后,对应的策略模型会自动运行,无需手动操作,满足条件后,自动对期权合约进行套利下单。


5、熊市看涨期权垂直套利

投资者看淡后市,因此卖出低履约价格的看涨期权收取较高的权利金,但是又担心市价会升破履约价格而出现不断增大的风险,因此,为了对冲市场上升的风险,可以买入一张较高履约价格的看涨期权,一旦市价升破较高履约价格,该看涨期权所得的价值可以抵消之前所卖出的看涨期权的风险。该策略的损益情况如下图所示:



①标的合约价格分析

编写策略模型,寻找处于下跌趋势的行情位置,作为套利入场的时机。



②选择期权合约,形成套利策略

根据自己对风险与收益的不同偏好,选择不同的期权合约,形成套利策略

Data
  data1:"m1709-C-2800"; //定义第一个看涨期权合约
  data2:"m1709-C-2850"; //定义第二个看涨期权合约
Params
  Numeric Lots(2); //定义手数
  Numeric Length1(5); //定义周期数
  Numeric Length2(10); //定义周期数
Vars
  Numeric MA1; //均线
  Numeric MA2; //均线
  Numeric OptionStrikePrice1; //行权价
  Numeric OptionStrikePrice2; //行权价
  Numeric SKfun; //SK条件
  Numeric BPfun; //BP条件

Begin
  MA1 = Ma(Close,Length1);        //5周期均线
  MA2 = Ma(Close,Length2);        //10周期均线

  SKfun = CrossDown(MA1,MA2); //5周期均线下穿10周期均线
  BPfun = CrossUp(MA1,MA2);     //5周期均线上穿10周期均线

  OptionStrikePrice1 = data1.Price("StrikePrice");//取data1合约的行权价
  OptionStrikePrice2 = data2.Price("StrikePrice");//取data2合约的行权价

  If(SKfun && OptionStrikePrice2 > OptionStrikePrice1 && GetGlobalVar(0) == 0)//满足SK信号条件并且合约2的行权价>合约1的行权价
  {
  data2.A_SendOrder(Enum_Buy,Enum_Entry,Lots,data2.Price("Ask1"));//对合约2以卖价1 的价格发出买2手开仓委托
  data1.A_SendOrder(Enum_Sell,Enum_Entry,Lots,data1.Price("Bid1"));//对合约1以买价1 的价格发出卖2手开仓委托
  SetGlobalVar(0,1);//全局变量为1
  }

  If(BPfun && data2.F_BuyRemainPosition() > 0 && data1.F_SellRemainPosition() > 0&& GetGlobalVar(0) == 1)//满足BP信号并且合约2有多头持仓并且合约1有空头持仓
  {
  data2.A_SendOrder(Enum_Sell,Enum_Exit,data2.F_BuyRemainPosition(),data2.Price("Bid1"));//对合约2以买价1 的价格发出全卖平的委托
  data1.A_SendOrder(Enum_Buy,Enum_Exit,data1.F_SellRemainPosition(),data1.Price("Ask1"));//对合约1以卖价1 的价格发出全买平的委托
     SetGlobalVar(0,0);//全局变量为0
  }

End


③策略运行

将策略模型在K线主图进行计算后加载到运行模组中,用来判断标的物的价格趋势,寻找套利的入场时机。加载成功后,对应的策略模型会自动运行,无需手动操作,满足条件后,自动对期权合约进行套利下单。


6、熊市看跌期权垂直套利

该策略是看淡后市的保守投资策略。投资者看淡后市,但认为后市未必后大跌,反而市况可能会进入反复调整,因此时间价值的损耗可能对单独买入看跌期权不利。另外,投资者希望可以减少支出,如果再卖出一份看跌期权,则可收取权利金。于是,买入高履约价格的看跌期权,同时又卖出低履约价格的看跌期权。该策略的损益情况如下图所示:



①标的合约价格分析

编写策略模型,寻找上涨趋势的尾部,行情反转,震荡下跌的位置,作为套利入场的时机。



②选择期权合约,形成套利策略

根据自己对风险与收益的不同偏好,选择不同的期权合约,形成套利策略

Data
  data1:"m1709-P-2800"; //定义第一个看跌期权合约
  data2:"m1709-P-2850"; //定义第二个看跌期权合约
Params
  Numeric Lots(2); //定义手数
  Numeric Length1(5); //定义周期数
  Numeric Length2(10); //定义周期数
Vars
  Numeric MA1; //均线
  Numeric MA2; //均线
  Numeric OptionStrikePrice1; //行权价
  Numeric OptionStrikePrice2; //行权价
  Numeric SKfun; //SK条件
  Numeric BPfun; //BP条件
Begin
  MA1 = Ma(Close,Length1);    //5周期均线
  MA2 = Ma(Close,Length2);    //10周期均线

  SKfun = CrossDown(MA1,MA2);//5周期均线下穿10周期均线
  BPfun = CrossUp(MA1,MA2);    //5周期均线上穿10周期均线

  OptionStrikePrice1 = data1.Price("StrikePrice");//取data1合约的行权价
  OptionStrikePrice2 = data2.Price("StrikePrice");//取data2合约的行权价

  If(SKfun && OptionStrikePrice2 > OptionStrikePrice1 && GetGlobalVar(0) == 0)//满足SK信号条件并且合约2的行权价>合约1的行权价
  {
  data2.A_SendOrder(Enum_Buy,Enum_Entry,Lots,data2.Price("Ask1"));//对合约2以卖价1 的价格发出买2手开仓委托
  data1.A_SendOrder(Enum_Sell,Enum_Entry,Lots,data1.Price("Bid1"));//对合约1以买价1 的价格发出卖2手开仓委托
  SetGlobalVar(0,1);//全局变量为1
  }

  If(BPfun && data2.F_BuyRemainPosition() > 0 && data1.F_SellRemainPosition() > 0&&GetGlobalVar(0) == 1)//满足BP信号并且合约2有多头持仓并且合约1有空头持仓
  {
  data2.A_SendOrder(Enum_Sell,Enum_Exit,data2.F_BuyRemainPosition(),data2.Price("Bid1"));//对合约2以买价1 的价格发出全卖平的委托
  data1.A_SendOrder(Enum_Buy,Enum_Exit,data1.F_SellRemainPosition(),data1.Price("Ask1"));//对合约1以卖价1 的价格发出全买平的委托
  SetGlobalVar(0,0);//全局变量为0
  }

End


③策略运行

将策略模型在K线主图进行计算后加载到运行模组中,用来判断标的物的价格趋势,寻找套利的入场时机。加载成功后,对应的策略模型会自动运行,无需手动操作,满足条件后,自动对期权合约进行套利下单。


前言

  不管是主观交易还是程序化交易,很多成功的交易者每年算一笔账,会发现在滑点上的损失比交的手续费还要多。随着投资者对程序化交易认识不断提高,投资者之间将逐渐从交易策略的较量转变为下单精细化控制的博弈上来。

  算法交易模型可实现对委托的每个环节进行精细化控制,达到降低交易成本的目的,如何减小滑点成本,减小交易的摩擦成本是他们的着力点。算法交易模型根据盘口价格变化、资金流向、多空对比对之后瞬间价格变化的一个判断来进行交易,适用于复杂类型的思路或个性化的交易精细控制,对账户整体做风控等应用。

优势

  • 个性化的交易精细控制
  • 个性化的模组信号执行控制
  • 可实现高频交易、锁仓等复杂思路

案例

案例一:手动下单调用算法交易

对于大资金客户,是否有下单单量大,唯恐惊扰行情趋势的困扰?是否有欲拉动行情却缕缕付出了大量成本的困扰?这里我们以此为例通过手动下单调用算法交易的方式介绍两个案例来解决你的困扰。


策略1:通过算法根据买卖量,自动拆分大单


交易思路解析:

1、取得手动下单的合约、手数、买卖方向、开平方向;自设委托价格。

2、如果为开仓:

(1)如果为买入开仓:

分批买入,取盘口卖一量*1/2与剩余下单手数比较,取数量较小值根据盘口卖一价买入,第一批成交后,再委托第二批,直至完全成交后退出算法。

(2)如果为卖出开仓:

分批卖出,取盘口买一量*1/2与剩余下单手数比较,取数量较小值根据盘口买一价卖出,第一批成交后,再委托第二批,直至完全成交后退出算法。

3、如果为平仓:

(1)如果为买入平仓:

同买入开仓。

如果合约为上海持仓,平仓时判断今仓,优先平今。

(2)如果为卖出平仓:

同卖出开仓。

如果合约为上海持仓,平仓时判断今仓,优先平今。


源码实现:

Vars
    Global_String TAC; //账号
    Global_String COD; //合约编码
    Global_Numeric TBS; //买卖方向
    Global_Numeric TEE; //开平方向
    Global_Numeric TVL; //交易手数
    Global_Numeric P; //数值比例
    Global_Numeric NEWP; //最新价
    Global_Numeric RLP; //涨停价
    Global_Numeric FLP; //跌停价
    Global_Numeric BIDP; //买一价
    Global_Numeric ASKP; //卖一价
    Global_Numeric BIDV; //买一量
    Global_Numeric ASKV; //卖一量
    Global_Numeric SH; //上海合约标志
    Global_Numeric MINP; //最小变动价位
    Global_Numeric BRP; //多头可用持仓
    Global_Numeric SRP; //空头可用持仓
    Global_Numeric BRP0; //多头今仓可用持仓
    Global_Numeric SRP0; //空头今仓可用持仓
    Global_Numeric BRP1; //多头老仓可用持仓
    Global_Numeric SRP1; //空头老仓可用持仓
    Global_Numeric BKID; //买开委托
    Global_Numeric SKID; //卖开委托
    Global_Numeric BPID0; //买平今仓委托
    Global_Numeric BPID1; //买平老仓委托
    Global_Numeric BPID; //买平委托
    Global_Numeric SPID0; //卖平今仓委托
    Global_Numeric SPID1; //卖平老仓委托
    Global_Numeric SPID; //卖平委托
    Global_Numeric BKFLG; //买开标志
    Global_Numeric SKFLG; //卖开标志
    Global_Numeric BPFLG0; //买平今仓标志
    Global_Numeric BPFLG1; //买平老仓标志
    Global_Numeric BPFLG; //买平标志
    Global_Numeric SPFLG0; //卖平今仓标志
    Global_Numeric SPFLG1; //卖平老仓标志
    Global_Numeric SPFLG; //卖平标志
    Global_Numeric BKM; //买开委托手数
    Global_Numeric SKM; //卖开委托手数
    Global_Numeric BPM0; //买平今仓委托手数
    Global_Numeric BPM1; //买平老仓委托手数
    Global_Numeric BPM; //买平委托手数
    Global_Numeric SPM0; //卖平今仓委托手数
    Global_Numeric SPM1; //卖平老仓委托手数
    Global_Numeric SPM; //卖平委托手数
    Global_Numeric BPR; //买方向委托价格
    Global_Numeric SPR; //卖方向委托价格
    Global_Numeric KPFLG; //委托处理标志
Begin
    //------------------------------------委托获取------------------------------------//
    If(KPFLG == 0) //如果未开启委托处理
    {
       TAC = M_GetTradeAccount(0); //账号
       COD = M_GetTradeContract(); //合约编码
       TBS = M_GetTradeBuySell(); //买卖方向
       TEE = M_GetTradeEntryExit(); //开平方向
       TVL = M_GetTradeVol(); //交易手数
       If(TVL > 0 && COD.A_IsExchangeOpen() == 1) //如果交易手数大于0,且当前状态是开盘
       {
          If(TBS == Enum_Buy && TEE == Enum_Entry) //如果是买开方向
          {
             Commentary("【" + COD + "做多:手动买开" + Text(TVL) + "手!】");
             KPFLG = 1; //开启买开委托处理
          }
          Else If(TBS == Enum_Sell && TEE == Enum_Entry) //如果是卖开方向
          {
             Commentary("【" + COD + "做空:手动卖开" + Text(TVL) + "手!】");
             KPFLG = 2; //开启卖开委托处理
          }
          Else If(TBS == Enum_Buy && TEE == Enum_Exit) //如果是买平方向
          {
             Commentary("【" + COD + "平空:手动买平" + Text(TVL) + "手!】");
             KPFLG = 3; //开启买平委托处理
          }
          Else If(TBS == Enum_Buy && TEE == Enum_ExitToday) //如果是买平今方向
          {
             Commentary("【" + COD + "平今空:手动买平" + Text(TVL) + "手!】");
             KPFLG = 4; //开启买平今仓委托处理
          }
          Else If(TBS == Enum_Sell && TEE == Enum_Exit) //如果是卖平方向
          {
             Commentary("【" + COD + "平多:手动卖平" + Text(TVL) + "手!】");
             KPFLG = 5; //开启卖平委托处理
          }
          Else If(TBS == Enum_Sell && TEE == Enum_ExitToday) //如果是卖平今方向
          {
             Commentary("【" + COD + "平今多:手动卖平" + Text(TVL) + "手!】");
             KPFLG = 6; //开启卖平今仓委托处理
          }
          P = 0.5; //数值比例
          SH = COD.A_IsSHCode(); //上海合约标志
          MINP = COD.Price("MinPrice"); //最小变动价位
         
       }
    }
    //------------------------------------委托处理------------------------------------//

   If(KPFLG > 0 && COD.A_IsExchangeOpen() == 1) //如果已开启委托处理,且当前状态是开盘
    {
       BIDP = COD.Price("Bid1"); //买一价
       ASKP = COD.Price("Ask1"); //卖一价
       BIDV = COD.Price("BidVol1"); //买一量
       ASKV = COD.Price("AskVol1"); //卖一量
       BPR= ASKP; //买方向委托价格设为卖一价
       SPR = BIDP; //卖方向委托价格设为买一价
       BRP = COD.A_BuyRemainPosition(); //多头可用持仓
       SRP = COD.A_SellRemainPosition(); //空头可用持仓
       If(SH == 1) //如果当前合约是上海市场合约
       {
          BRP0 = COD.A_TodayBuyRemainPosition(); //多头今仓可用持仓
          SRP0 = COD.A_TodaySellRemainPosition(); //空头今仓可用持仓
          BRP1 = BRP - BRP0; //多头老仓可用持仓
          SRP1 = SRP - SRP0; //空头老仓可用持仓
       }
       //------------------------------------成交判断------------------------------------//
       If(BKFLG == 1) //如果有买开委托
       {
          If(F_OrderStatus(BKID) == Enum_Filled) //如果买开委托成交
          {
             Commentary("【" + COD + "做多:买开委托成交!】");
             TVL = TVL - BKM; //交易手数自减买开委托手数
             BKFLG = 0; //买开标志归0
          }
       }
       If(SKFLG == 1) //如果有卖开委托
       {
          If(F_OrderStatus(SKID) == Enum_Filled) //如果卖开委托成交
          {
             Commentary("【" + COD + "做空:卖开委托成交!】");
             TVL = TVL - SKM; //交易手数自减卖开委托手数
             SKFLG = 0; //卖开标志归0
          }
       }
       If(BPFLG0 == 1) //如果有买平今仓委托
       {
          If(F_OrderStatus(BPID0) == Enum_Filled) //如果买平今仓委托成交
          {
             Commentary("【" + COD + "平空:买平今仓委托成交!】");
             TVL = TVL - BPM0; //交易手数自减买平今仓委托手数
             BPFLG0 = 0; //买平今仓标志归0
          }
       }
       If(BPFLG1 == 1) //如果有买平老仓委托
       {
          If(F_OrderStatus(BPID1) == Enum_Filled) //如果买平老仓委托成交
          {
             Commentary("【" + COD + "平空:买平老仓委托成交!】");
             TVL = TVL - BPM1; //交易手数自减买平老仓委托手数
             BPFLG1 = 0; //买平老仓标志归0
          }
       }
       If(BPFLG == 1) //如果有买平委托
       {
          If(F_OrderStatus(BPID) == Enum_Filled) //如果买平委托成交
          {
             Commentary("【" + COD + "平空:买平委托成交!】");
             TVL = TVL - BPM; //交易手数自减买平委托手数
             BPFLG = 0; //买平标志归0
          }
       }
       If(SPFLG0 == 1) //如果有卖平今仓委托
       {
          If(F_OrderStatus(SPID0) == Enum_Filled) //如果卖平今仓委托成交
          {
             Commentary("【" + COD + "平多:卖平今仓委托成交!】");
             TVL = TVL - SPM0; //交易手数自减卖平今仓委托手数
             SPFLG0 = 0; //卖平今仓标志归0
          }
       }
       If(SPFLG1 == 1) //如果有卖平老仓委托
       {
          If(F_OrderStatus(SPID1) == Enum_Filled) //如果卖平老仓委托成交
          {
             Commentary("【" + COD + "平多:卖平老仓委托成交!】");
             TVL = TVL - SPM1; //交易手数自减卖平老仓委托手数
             SPFLG1 = 0; //卖平老仓标志归0
          }
       }
       If(SPFLG == 1) //如果有卖平委托
       {
          If(F_OrderStatus(SPID) == Enum_Filled) //如果卖平委托成交
          {
             Commentary("【" + COD + "平多:卖平委托成交!】");
             TVL = TVL - SPM; //交易手数自减卖平委托手数
             SPFLG = 0; //卖平标志归0
          }
       }
       //------------------------------------委托处理------------------------------------//
       If(KPFLG == 1) //如果已开启买开委托处理
       {
          If(BKFLG == 0) //如果没有买开委托
          {
             BKM = Min(Ceiling(P * ASKV,1),TVL); //买开委托手数
             If(BKM > 0) //如果买开委托手数大于0
             {
                Commentary("【" + COD + "做多:公式买开" + Text(BKM) + "手!】");
                Commentary("【" + COD + "做多:买开委托发出!】");
                BKID = COD.A_SendOrder(Enum_Buy,Enum_Entry,BKM,BPR,TAC); //以买开委托价格发出买开委托手数的买开委托
                BKFLG = 1; //已发出买开委托
             }
             Else If(BKM == 0) //如果买开委托手数等于0
             {
                Commentary("【" + COD + "做多:买开委托完成!】");
                Exit(); //退出公式
             }
          }
       }
       Else If(KPFLG == 2) //如果已开启卖开委托处理
       {
          If(SKFLG == 0) //如果没有卖开委托
          {
             SKM = Min(Ceiling(P * BIDV,1),TVL); //卖开委托手数
             If(SKM > 0) //如果卖开委托手数大于0
             {
                Commentary("【" + COD + "做空:公式卖开" + Text(SKM) + "手!】");
                Commentary("【" + COD + "做空:卖开委托发出!】");
                SKID = COD.A_SendOrder(Enum_Sell,Enum_Entry,SKM,SPR,TAC); //以卖开委托价格发出卖开委托手数的卖开委托
                SKFLG = 1; //已发出卖开委托
             }
             Else If(SKM == 0) //如果卖开委托手数等于0
             {
                Commentary("【" + COD + "做空:卖开委托完成!】");
                Exit(); //退出公式
             }
          }
       }

      Else If(KPFLG == 3 || KPFLG == 4) //如果已开启买平委托处理
       {
          If(BPFLG0 == 0 && BPFLG1 == 0 && BPFLG == 0) //如果没有买平委托
          {
             BPM = Min(Ceiling(P * ASKV,1),TVL); //买平委托手数
             If(BPM > 0) //如果买平委托手数大于0
             {
                If(SRP > 0) //如果有空头可用持仓
                {
                   If(SH == 1) //如果当前合约是上海市场合约
                   {
                      If(SRP0 > 0) //如果有空头今仓可用持仓
                      {
                         BPM0 = Min(SRP0,BPM); //买平今仓委托手数
                         BPM1 = Min(SRP1,BPM - BPM0); //买平老仓委托手数
                         Commentary("【" + COD + "平空:公式买平今仓" + Text(BPM0) + "手!】");
                         Commentary("【" + COD + "平空:买平今仓委托发出!】");
                         BPID0 = COD.A_SendOrder(Enum_Buy,Enum_ExitToday,BPM0,BPR,TAC); //以买平委托价格发出买平今仓委托手数的买平今仓委托
                         BPFLG0 = 1; //已发出买平今仓委托
                      }
                      Else If(SRP0 == 0) //如果没有空头今仓可用持仓
                      {
                         BPM1 = Min(SRP1,BPM); //买平老仓委托手数
                      }
                      If(KPFLG == 3 && SRP1 > 0 && BPM1 > 0) //如果未开启买平今仓委托处理,且有空头老仓可用持仓,且买平老仓委托手数大于0
                      {
                         Commentary("【" + COD + "平空:公式买平老仓" + Text(BPM1) + "手!】");
                         Commentary("【" + COD + "平空:买平老仓委托发出!】");
                         BPID1 = COD.A_SendOrder(Enum_Buy,Enum_Exit,BPM1,BPR,TAC); //以买平委托价格发出买平老仓委托手数的买平老仓委托
                         BPFLG1 = 1; //已发出买平老仓委托
                      }
                   }
                   Else //如果当前合约非上海市场合约
                   {
                      BPM = Min(SRP,BPM); //买平委托手数
                      Commentary("【" + COD + "平空:公式买平" + Text(BPM) + "手!】");
                      Commentary("【" + COD + "平空:买平委托发出!】");
                      BPID = COD.A_SendOrder(Enum_Buy,Enum_Exit,BPM,BPR,TAC); //以买平委托价格发出买平委托手数的买平委托
                      BPFLG = 1; //已发出买平委托
                   }
                }
                Else If(SRP == 0) //如果没有空头可用持仓
                {
                   Commentary("【" + COD + "平空:买平委托完成!】");
                   Exit(); //退出公式
                }
             }
             Else If(BPM == 0) //如果买平委托手数等于0
             {
                Commentary("【" + COD + "平空:买平委托完成!】");
                Exit(); //退出公式
             }
          }
       }

      Else If(KPFLG == 5 || KPFLG == 6) //如果已开启卖平委托处理
       {
          If(SPFLG0 == 0 && SPFLG1 == 0 && SPFLG == 0) //如果没有卖平委托
          {
             SPM = Min(Ceiling(P * BIDV,1),TVL); //卖平委托手数
             If(SPM > 0) //如果卖平委托手数大于0
             {
                If(BRP > 0) //如果有多头可用持仓
                {
                   If(SH == 1) //如果当前合约是上海市场合约
                   {
                      If(BRP0 > 0) //如果有多头今仓可用持仓
                      {
                         SPM0 = Min(BRP0,SPM); //卖平今仓委托手数
                         SPM1 = Min(BRP1,SPM - SPM0); //卖平老仓委托手数
                         Commentary("【" + COD + "平多:公式卖平今仓" + Text(SPM0) + "手!】");
                         Commentary("【" + COD + "平多:卖平今仓委托发出!】");
                         SPID0 = COD.A_SendOrder(Enum_Sell,Enum_ExitToday,SPM0,SPR,TAC); //以卖平委托价格发出卖平今仓委托手数的卖平今仓委托
                         SPFLG0 = 1; //已发出卖平今仓委托
                      }
                      Else If(BRP0 == 0) //如果没有多头今仓可用持仓
                      {
                         SPM1 = Min(BRP1,SPM); //卖平老仓委托手数
                      }
                      If(KPFLG == 5 && BRP1 > 0 && SPM1 > 0) //如果未开启卖平今仓委托处理,且有多头老仓可用持仓,且卖平老仓委托手数大于0
                      {
                         Commentary("【" + COD + "平多:公式卖平老仓" + Text(SPM1) + "手!】");
                         Commentary("【" + COD + "平多:卖平老仓委托发出!】");
                         SPID1 = COD.A_SendOrder(Enum_Sell,Enum_Exit,SPM1,SPR,TAC); //以卖平委托价格发出卖平老仓委托手数的卖平老仓委托
                         SPFLG1 = 1; //已发出卖平老仓委托
                      }
                   }
                   Else //如果当前合约非上海市场合约
                   {
                      SPM = Min(BRP,SPM); //卖平委托手数
                      Commentary("【" + COD + "平多:公式卖平" + Text(SPM) + "手!】");
                      Commentary("【" + COD + "平多:卖平委托发出!】");
                      SPID = COD.A_SendOrder(Enum_Sell,Enum_Exit,SPM,SPR,TAC); //以卖平委托价格发出卖平委托手数的卖平委托
                      SPFLG = 1; //已发出卖平委托
                   }
                }
                Else If(BRP == 0) //如果没有多头可用持仓
                {
                   Commentary("【" + COD + "平多:卖平委托完成!】");
                   Exit(); //退出公式
                }
             }
             Else If(SPM == 0) //如果卖平委托手数等于0
             {
                Commentary("【" + COD + "平多:卖平委托完成!】");
                Exit(); //退出公式
             }
          }
       }
    }
End


通过快捷键F12或软件右上方菜单[账户]->期货账户->算法交易下单,调出算法交易下单界面,按下图所示方法实现手动下单调用算法交易:


注:①如果再次下单,会再次加载该模型,不会直接在前一个算法交易模型中继续执行。

②如果模型中含有函数Exit(),则模型执行完毕后会自动退出。


策略2:拉动行情,每次自动把买盘吃光,将行情往上拉,反之亦然。


交易思路解析:

1、取值手动下单的合约、手数、买卖方向、开平方向;自设委托价格。

2、如果为开仓:

(1)如果为买入开仓:

分批买入,取盘口卖一量与剩余下单手数比较,取数量较小值根据盘口卖一价买入。

如果盘口卖一价变化,撤单并重新委托,取盘口卖一量与该批剩余下单手数比较,取数量较小值根据盘口卖一价买入,直至完全成交完全成交后退出算法。

(2)如果为卖出开仓:

分批卖出,取盘口买一量与剩余下单手数比较,取数量较小值根据盘口买一价卖出。

如果盘口买一价变化,撤单并重新委托,取盘口买一量与该批剩余下单手数比较,取数量较小值根据盘口买一价卖出,直至完全成交完全成交后退出算法。

3、如果为平仓:

(1)如果为买入平仓:

同买入开仓。

如果合约为上海持仓,平仓时判断今仓,优先平今。

(2)如果为卖出开仓:

同卖出开仓。

如果合约为上海持仓,平仓时判断今仓,优先平今。


源码实现:

Vars
    Global_String TAC; //账号
    Global_String COD; //合约编码
    Global_Numeric TBS; //买卖方向
    Global_Numeric TEE; //开平方向
    Global_Numeric TVL; //交易手数
    Global_Numeric BIDP; //买一价
    Global_Numeric ASKP; //卖一价
    Global_Numeric BIDV; //买一量
    Global_Numeric ASKV; //卖一量
    Global_Numeric SH; //上海合约标志
    Global_Numeric MINP; //最小变动价位
    Global_Numeric BRP; //多头可用持仓
    Global_Numeric SRP; //空头可用持仓
    Global_Numeric BRP0; //多头今仓可用持仓
    Global_Numeric SRP0; //空头今仓可用持仓
    Global_Numeric BRP1; //多头老仓可用持仓
    Global_Numeric SRP1; //空头老仓可用持仓
    Global_Numeric BKID; //买开委托
    Global_Numeric SKID; //卖开委托
    Global_Numeric BPID0; //买平今仓委托
    Global_Numeric BPID1; //买平老仓委托
    Global_Numeric BPID; //买平委托
    Global_Numeric SPID0; //卖平今仓委托
    Global_Numeric SPID1; //卖平老仓委托
    Global_Numeric SPID; //卖平委托
    Global_Numeric BKFLG; //买开标志
    Global_Numeric SKFLG; //卖开标志
    Global_Numeric BPFLG0; //买平今仓标志
    Global_Numeric BPFLG1; //买平老仓标志
    Global_Numeric BPFLG; //买平标志
    Global_Numeric SPFLG0; //卖平今仓标志
    Global_Numeric SPFLG1; //卖平老仓标志
    Global_Numeric SPFLG; //卖平标志
    Global_Numeric BKDEL; //买开撤单标志
    Global_Numeric SKDEL; //卖开撤单标志
    Global_Numeric BPDEL0; //买平今仓撤单标志
    Global_Numeric BPDEL1; //买平老仓撤单标志
    Global_Numeric BPDEL; //买平撤单标志
    Global_Numeric SPDEL0; //卖平今仓撤单标志
    Global_Numeric SPDEL1; //卖平老仓撤单标志
    Global_Numeric SPDEL; //卖平撤单标志
    Global_Numeric BKM; //买开委托手数
    Global_Numeric SKM; //卖开委托手数
    Global_Numeric BPM0; //买平今仓委托手数
    Global_Numeric BPM1; //买平老仓委托手数
    Global_Numeric BPM; //买平委托手数
    Global_Numeric SPM0; //卖平今仓委托手数
    Global_Numeric SPM1; //卖平老仓委托手数
    Global_Numeric SPM; //卖平委托手数
    Global_Numeric BKP; //买开委托价格
    Global_Numeric SKP; //卖开委托价格
    Global_Numeric BPP0; //买平今仓委托价格
    Global_Numeric BPP1; //买平老仓委托价格
    Global_Numeric BPP; //买平委托价格
    Global_Numeric SPP0; //卖平今仓委托价格
    Global_Numeric SPP1; //卖平老仓委托价格
    Global_Numeric SPP; //卖平委托价格
    Global_Numeric KPFLG; //委托处理标志
Begin
    //------------------------------------委托获取------------------------------------//
    If(KPFLG == 0) //如果未开启委托处理
    {
       TAC = M_GetTradeAccount(0); //账号
       COD = M_GetTradeContract(); //合约编码
       TBS = M_GetTradeBuySell(); //买卖方向
       TEE = M_GetTradeEntryExit(); //开平方向
       TVL = M_GetTradeVol(); //交易手数
       If(TVL > 0 && COD.A_IsExchangeOpen() == 1) //如果交易手数大于0,且当前状态是开盘
       {
          If(TBS == Enum_Buy && TEE == Enum_Entry) //如果是买开方向
          {
             Commentary("【" + COD + "做多:手动买开" + Text(TVL) + "手!】");
             KPFLG = 1; //开启买开委托处理
          }
          Else If(TBS == Enum_Sell && TEE == Enum_Entry) //如果是卖开方向
          {
             Commentary("【" + COD + "做空:手动卖开" + Text(TVL) + "手!】");
             KPFLG = 2; //开启卖开委托处理
          }
          Else If(TBS == Enum_Buy && TEE == Enum_Exit) //如果是买平方向
          {
             Commentary("【" + COD + "平空:手动买平" + Text(TVL) + "手!】");
             KPFLG = 3; //开启买平委托处理
          }
          Else If(TBS == Enum_Buy && TEE == Enum_ExitToday) //如果是买平今方向
          {
             Commentary("【" + COD + "平今空:手动买平" + Text(TVL) + "手!】");
             KPFLG = 4; //开启买平今仓委托处理
          }
          Else If(TBS == Enum_Sell && TEE == Enum_Exit) //如果是卖平方向
          {
             Commentary("【" + COD + "平多:手动卖平" + Text(TVL) + "手!】");
             KPFLG = 5; //开启卖平委托处理
          }
          Else If(TBS == Enum_Sell && TEE == Enum_ExitToday) //如果是卖平今方向
          {
             Commentary("【" + COD + "平今多:手动卖平" + Text(TVL) + "手!】");
             KPFLG = 6; //开启卖平今仓委托处理
          }
          SH = COD.A_IsSHCode(); //上海合约标志
          MINP = COD.Price("MinPrice"); //最小变动价位
       }
    }

   //------------------------------------委托处理------------------------------------//
    If(KPFLG > 0 && COD.A_IsExchangeOpen() == 1) //如果已开启委托处理,且当前状态是开盘
    {
       BIDP = COD.Price("Bid1"); //买一价
       ASKP = COD.Price("Ask1"); //卖一价
       BIDV = COD.Price("BidVol1"); //买一量
       ASKV = COD.Price("AskVol1"); //卖一量
       BRP = COD.A_BuyRemainPosition(); //多头可用持仓
       SRP = COD.A_SellRemainPosition(); //空头可用持仓
       If(SH == 1) //如果当前合约是上海市场合约
       {
          BRP0 = COD.A_TodayBuyRemainPosition(); //多头今仓可用持仓
          SRP0 = COD.A_TodaySellRemainPosition(); //空头今仓可用持仓
          BRP1 = BRP - BRP0; //多头老仓可用持仓
          SRP1 = SRP - SRP0; //空头老仓可用持仓
       }
       //------------------------------------成交判断------------------------------------//
       If(BKFLG == 1) //如果有买开委托
       {
          If(F_OrderStatus(BKID) == Enum_Filled) //如果买开委托成交
          {
             Commentary("【" + COD + "做多:买开委托成交!】");
             TVL = TVL - BKM; //交易手数自减买开委托手数
             BKFLG = 0; //买开标志归0
             BKDEL = 0; //买开撤单标志归0
          }
          Else If(F_OrderStatus(BKID) == Enum_Canceled) //如果买开委托被撤单
          {
             If(F_OrderFilledLot(BKID) > 0) //如果买开委托部分成交
             {
                BKM = BKM - F_OrderFilledLot(BKID); //买开委托手数自减成交手数
                TVL = TVL - F_OrderFilledLot(BKID); //交易手数自减成交手数
             }
             BKM = Min(ASKV,BKM); //买开委托手数
             If(BKM > 0) //如果买开委托手数大于0
             {
                Commentary("【" + COD + "做多:公式买开" + Text(BKM) + "手!】");
                Commentary("【" + COD + "做多:买开委托追价!】");
                BKP = ASKP; //买开委托价格设为卖一价
                BKID = COD.A_SendOrder(Enum_Buy,Enum_Entry,BKM,BKP,TAC); //以买开委托价格发出买开委托手数的买开委托
             }
             BKDEL = 0; //买开撤单标志归0
          }
          Else If(F_OrderStatus(BKID) == Enum_Declared || F_OrderStatus(BKID) == Enum_FillPart) //如果买开委托全部挂单或部分成交
          {
             If(ASKP != BKP && BKDEL == 0) //如果卖一价不等于买开委托价格,且未撤单
             {
                Commentary("【" + COD + "做多:买开委托撤单!】");
                F_DeleteOrder(F_OrderContractNo(BKID)); //撤掉买开委托挂单
                BKDEL = 1; //已发出撤掉买开委托挂单
             }
          }
       }
       If(SKFLG == 1) //如果有卖开委托
       {
          If(F_OrderStatus(SKID) == Enum_Filled) //如果卖开委托成交
          {
             Commentary("【" + COD + "做空:卖开委托成交!】");
             TVL = TVL - SKM; //交易手数自减卖开委托手数
             SKFLG = 0; //卖开标志归0
             SKDEL = 0; //卖开撤单标志归0
          }
          Else If(F_OrderStatus(SKID) == Enum_Canceled) //如果卖开委托被撤单
          {
             If(F_OrderFilledLot(SKID) > 0) //如果卖开委托部分成交
             {
                SKM = SKM - F_OrderFilledLot(SKID); //卖开委托手数自减成交手数
                TVL = TVL - F_OrderFilledLot(SKID); //交易手数自减成交手数
             }
             SKM = Min(BIDV,SKM); //卖开委托手数
             If(SKM > 0) //如果卖开委托手数大于0
             {
                Commentary("【" + COD + "做空:公式卖开" + Text(SKM) + "手!】");
                Commentary("【" + COD + "做空:卖开委托追价!】");
                SKP = BIDP; //卖开委托价格设为买一价
                SKID = COD.A_SendOrder(Enum_Sell,Enum_Entry,SKM,SKP,TAC); //以卖开委托价格发出卖开委托手数的卖开委托
             }
             SKDEL = 0; //卖开撤单标志归0
          }

         Else If(F_OrderStatus(SKID) == Enum_Declared || F_OrderStatus(SKID) == Enum_FillPart) //如果卖开委托全部挂单或部分成交
          {
             If(BIDP != SKP && SKDEL == 0) //如果买一价不等于卖开委托价格,且未撤单
             {
                Commentary("【" + COD + "做空:卖开委托撤单!】");
                F_DeleteOrder(F_OrderContractNo(SKID)); //撤掉卖开委托挂单
                SKDEL = 1; //已发出撤掉卖开委托挂单
             }
          }
       }
       If(BPFLG0 == 1) //如果有买平今仓委托
       {
          If(F_OrderStatus(BPID0) == Enum_Filled) //如果买平今仓委托成交
          {
             Commentary("【" + COD + "平空:买平今仓委托成交!】");
             TVL = TVL - BPM0; //交易手数自减买平今仓委托手数
             BPFLG0 = 0; //买平今仓标志归0
             BPDEL0 = 0; //买平今仓撤单标志归0
          }
          Else If(F_OrderStatus(BPID0) == Enum_Canceled) //如果买平今仓委托被撤单
          {
             If(F_OrderFilledLot(BPID0) > 0) //如果买平今仓委托部分成交
             {
                BPM0 = BPM0 - F_OrderFilledLot(BPID0); //买平今仓委托手数自减成交手数
                TVL = TVL - F_OrderFilledLot(BPID0); //交易手数自减成交手数
             }
             BPM0 = Min1(SRP0,ASKV,BPM0); //买平今仓委托手数
             If(SRP0 > 0 && BPM0 > 0) //如果有空头今仓可用持仓,且买平今仓委托手数大于0
             {
                Commentary("【" + COD + "平空:公式买平今仓" + Text(BPM0) + "手!】");
                Commentary("【" + COD + "平空:买平今仓委托追价!】");
                BPP0 = ASKP; //买平今仓委托价格设为卖一价
                BPID0 = COD.A_SendOrder(Enum_Buy,Enum_ExitToday,BPM0,BPP0,TAC); //以买平今仓委托价格发出买平今仓委托手数的买平今仓委托
             }
             BPDEL0 = 0; //买平今仓撤单标志归0
          }
          Else If(F_OrderStatus(BPID0) == Enum_Declared || F_OrderStatus(BPID0) == Enum_FillPart) //如果买平今仓委托全部挂单或部分成交
          {
             If(ASKP != BPP0 && BPDEL0 == 0) //如果卖一价不等于买平今仓委托价格,且未撤单
             {
                Commentary("【" + COD + "平空:买平今仓委托撤单!】");
                F_DeleteOrder(F_OrderContractNo(BPID0)); //撤掉买平今仓委托挂单
                BPDEL0 = 1; //已发出撤掉买平今仓委托挂单
             }
          }
       }
       If(BPFLG1 == 1) //如果有买平老仓委托
       {
          If(F_OrderStatus(BPID1) == Enum_Filled) //如果买平老仓委托成交
          {
             Commentary("【" + COD + "平空:买平老仓委托成交!】");
             TVL = TVL - BPM1; //交易手数自减买平老仓委托手数
             BPFLG1 = 0; //买平老仓标志归0
             BPDEL1 = 0; //买平老仓撤单标志归0
          }
          Else If(F_OrderStatus(BPID1) == Enum_Canceled) //如果买平老仓委托被撤单
          {
             If(F_OrderFilledLot(BPID1) > 0) //如果买平老仓委托部分成交
             {
                BPM1 = BPM1 - F_OrderFilledLot(BPID1); //买平老仓委托手数自减成交手数
                TVL = TVL - F_OrderFilledLot(BPID1); //交易手数自减成交手数
             }
             BPM1 = Min1(SRP1,ASKV,BPM1); //买平老仓委托手数
             If(SRP1 > 0 && BPM1 > 0) //如果有空头老仓可用持仓,且买平老仓委托手数大于0
             {
                Commentary("【" + COD + "平空:公式买平老仓" + Text(BPM1) + "手!】");
                Commentary("【" + COD + "平空:买平老仓委托追价!】");
                BPP1 = ASKP; //买平老仓委托价格设为卖一价
                BPID1 = COD.A_SendOrder(Enum_Buy,Enum_Exit,BPM1,BPP1,TAC); //以买平老仓委托价格发出买平老仓委托手数的买平老仓委托
             }
             BPDEL1 = 0; //买平老仓撤单标志归0
          }

         Else If(F_OrderStatus(BPID1) == Enum_Declared || F_OrderStatus(BPID1) == Enum_FillPart) //如果买平老仓委托全部挂单或部分成交
          {
             If(ASKP != BPP1 && BPDEL1 == 0) //如果卖一价不等于买平老仓委托价格,且未撤单
             {
                Commentary("【" + COD + "平空:买平老仓委托撤单!】");
                F_DeleteOrder(F_OrderContractNo(BPID1)); //撤掉买平老仓委托挂单
                BPDEL1 = 1; //已发出撤掉买平老仓委托挂单
             }
          }
       }
       If(BPFLG == 1) //如果有买平委托
       {
          If(F_OrderStatus(BPID) == Enum_Filled) //如果买平委托成交
          {
             Commentary("【" + COD + "平空:买平委托成交!】");
             TVL = TVL - BPM; //交易手数自减买平委托手数
             BPFLG = 0; //买平标志归0
             BPDEL = 0; //买平撤单标志归0
          }
          Else If(F_OrderStatus(BPID) == Enum_Canceled) //如果买平委托被撤单
          {
             If(F_OrderFilledLot(BPID) > 0) //如果买平委托部分成交
             {
                BPM = BPM - F_OrderFilledLot(BPID); //买平委托手数自减成交手数
                TVL = TVL - F_OrderFilledLot(BPID); //交易手数自减成交手数
             }
             BPM = Min1(SRP,ASKV,BPM); //买平委托手数
             If(SRP > 0 && BPM > 0) //如果有空头可用持仓,且买平委托手数大于0
             {
                Commentary("【" + COD + "平空:公式买平" + Text(BPM) + "手!】");
                Commentary("【" + COD + "平空:买平委托追价!】");
                BPP = ASKP; //买平委托价格设为卖一价
                BPID = COD.A_SendOrder(Enum_Buy,Enum_Exit,BPM,BPP,TAC); //以买平委托价格发出买平委托手数的买平委托
             }
             BPDEL = 0; //买平撤单标志归0
          }
          Else If(F_OrderStatus(BPID) == Enum_Declared || F_OrderStatus(BPID) == Enum_FillPart) //如果买平委托全部挂单或部分成交
          {
             If(ASKP != BPP && BPDEL == 0) //如果卖一价不等于买平委托价格,且未撤单
             {
                Commentary("【" + COD + "平空:买平委托撤单!】");
                F_DeleteOrder(F_OrderContractNo(BPID)); //撤掉买平委托挂单
                BPDEL = 1; //已发出撤掉买平委托挂单
             }
          }
       }
       If(SPFLG0 == 1) //如果有卖平今仓委托
       {
          If(F_OrderStatus(SPID0) == Enum_Filled) //如果卖平今仓委托成交
          {
             Commentary("【" + COD + "平多:卖平今仓委托成交!】");
             TVL = TVL - SPM0; //交易手数自减卖平今仓委托手数
             SPFLG0 = 0; //卖平今仓标志归0
             SPDEL0 = 0; //卖平今仓撤单标志归0
          }
          Else If(F_OrderStatus(SPID0) == Enum_Canceled) //如果卖平今仓委托被撤单
          {
             If(F_OrderFilledLot(SPID0) > 0) //如果卖平今仓委托部分成交
             {
                SPM0 = SPM0 - F_OrderFilledLot(SPID0); //卖平今仓委托手数自减成交手数
                TVL = TVL - F_OrderFilledLot(SPID0); //交易手数自减成交手数
             }
             SPM0 = Min1(BRP0,BIDV,SPM0); //卖平今仓委托手数
             If(BRP0 > 0 && SPM0 > 0) //如果有多头今仓可用持仓,且卖平今仓委托手数大于0
             {
                Commentary("【" + COD + "平多:公式卖平今仓" + Text(SPM0) + "手!】");
                Commentary("【" + COD + "平多:卖平今仓委托追价!】");
                SPP0 = BIDP; //卖平今仓委托价格设为买一价
                SPID0 = COD.A_SendOrder(Enum_Sell,Enum_ExitToday,SPM0,SPP0,TAC); //以卖平今仓委托价格发出卖平今仓委托手数的卖平今仓委托
             }
             SPDEL0 = 0; //卖平今仓撤单标志归0
          }
          Else If(F_OrderStatus(SPID0) == Enum_Declared || F_OrderStatus(SPID0) == Enum_FillPart) //如果卖平今仓委托全部挂单或部分成交
          {
             If(BIDP != SPP0 && SPDEL0 == 0) //如果买一价不等于卖平今仓委托价格,且未撤单
             {
                Commentary("【" + COD + "平多:卖平今仓委托撤单!】");
                F_DeleteOrder(F_OrderContractNo(SPID0)); //撤掉卖平今仓委托挂单
                SPDEL0 = 1; //已发出撤掉卖平今仓委托挂单
             }
          }
       }

      If(SPFLG1 == 1) //如果有卖平老仓委托
       {
          If(F_OrderStatus(SPID1) == Enum_Filled) //如果卖平老仓委托成交
          {
             Commentary("【" + COD + "平多:卖平老仓委托成交!】");
             TVL = TVL - SPM1; //交易手数自减卖平老仓委托手数
             SPFLG1 = 0; //卖平老仓标志归0
             SPDEL1 = 0; //卖平老仓撤单标志归0
          }
          Else If(F_OrderStatus(SPID1) == Enum_Canceled) //如果卖平老仓委托被撤单
          {
             If(F_OrderFilledLot(SPID1) > 0) //如果卖平老仓委托部分成交
             {
                SPM1 = SPM1 - F_OrderFilledLot(SPID1); //卖平老仓委托手数自减成交手数
                TVL = TVL - F_OrderFilledLot(SPID1); //交易手数自减成交手数
             }
             SPM1 = Min1(BRP1,BIDV,SPM1); //卖平老仓委托手数
             If(BRP1 > 0 && SPM1 > 0) //如果有多头老仓可用持仓,且卖平老仓委托手数大于0
             {
                Commentary("【" + COD + "平多:公式卖平老仓" + Text(SPM1) + "手!】");
                Commentary("【" + COD + "平多:卖平老仓委托追价!】");
                SPP1 = BIDP; //卖平老仓委托价格设为买一价
                SPID1 = COD.A_SendOrder(Enum_Sell,Enum_Exit,SPM1,SPP1,TAC); //以卖平老仓委托价格发出卖平老仓委托手数的卖平老仓委托
             }
             SPDEL1 = 0; //卖平老仓撤单标志归0
          }
          Else If(F_OrderStatus(SPID1) == Enum_Declared || F_OrderStatus(SPID1) == Enum_FillPart) //如果卖平老仓委托全部挂单或部分成交
          {
             If(BIDP != SPP1 && SPDEL1 == 0) //如果买一价不等于卖平老仓委托价格,且未撤单
             {
                Commentary("【" + COD + "平多:卖平老仓委托撤单!】");
                F_DeleteOrder(F_OrderContractNo(SPID1)); //撤掉卖平老仓委托挂单
                SPDEL1 = 1; //已发出撤掉卖平老仓委托挂单
             }
          }
       }
       If(SPFLG == 1) //如果有卖平委托
       {
          If(F_OrderStatus(SPID) == Enum_Filled) //如果卖平委托成交
          {
             Commentary("【" + COD + "平多:卖平委托成交!】");
             TVL = TVL - SPM; //交易手数自减卖平委托手数
             SPFLG = 0; //卖平标志归0
             SPDEL = 0; //卖平撤单标志归0
          }
          Else If(F_OrderStatus(SPID) == Enum_Canceled) //如果卖平委托被撤单
          {
             If(F_OrderFilledLot(SPID) > 0) //如果卖平委托部分成交
             {
                SPM = SPM - F_OrderFilledLot(SPID); //卖平委托手数自减成交手数
                TVL = TVL - F_OrderFilledLot(SPID); //交易手数自减成交手数
             }
             SPM = Min1(BRP,BIDV,SPM); //卖平委托手数
             If(BRP > 0 && SPM > 0) //如果有多头可用持仓,且卖平委托手数大于0
             {
                Commentary("【" + COD + "平多:公式卖平" + Text(SPM) + "手!】");
                Commentary("【" + COD + "平多:卖平委托追价!】");
                SPP = BIDP; //卖平委托价格设为买一价
                SPID = COD.A_SendOrder(Enum_Sell,Enum_Exit,SPM,SPP,TAC); //以卖平委托价格发出卖平委托手数的卖平委托
             }
             SPDEL = 0; //卖平撤单标志归0
          }
          Else If(F_OrderStatus(SPID) == Enum_Declared || F_OrderStatus(SPID) == Enum_FillPart) //如果卖平委托全部挂单或部分成交
          {
             If(BIDP != SPP && SPDEL == 0) //如果买一价不等于卖平委托价格,且未撤单
             {
                Commentary("【" + COD + "平多:卖平委托撤单!】");
                F_DeleteOrder(F_OrderContractNo(SPID)); //撤掉卖平委托挂单
                SPDEL = 1; //已发出撤掉卖平委托挂单
             }
          }
       }
       //------------------------------------委托处理------------------------------------//
       If(KPFLG == 1) //如果已开启买开委托处理
       {
          If(BKFLG == 0) //如果没有买开委托
          {
             BKM = Min(ASKV,TVL); //买开委托手数
             If(BKM > 0) //如果买开委托手数大于0
             {
                Commentary("【" + COD + "做多:公式买开" + Text(BKM) + "手!】");
                Commentary("【" + COD + "做多:买开委托发出!】");
                BKP = ASKP; //买开委托价格设为卖一价
                BKID = COD.A_SendOrder(Enum_Buy,Enum_Entry,BKM,BKP,TAC); //以买开委托价格发出买开委托手数的买开委托
                BKFLG = 1; //已发出买开委托
             }
             Else If(BKM == 0) //如果买开委托手数等于0
             {
                Commentary("【" + COD + "做多:买开委托完成!】");
                Exit(); //退出公式
             }
          }
       }

      Else If(KPFLG == 2) //如果已开启卖开委托处理
       {
          If(SKFLG == 0) //如果没有卖开委托
          {
             SKM = Min(BIDV,TVL); //卖开委托手数
             If(SKM > 0) //如果卖开委托手数大于0
             {
                Commentary("【" + COD + "做空:公式卖开" + Text(SKM) + "手!】");
                Commentary("【" + COD + "做空:卖开委托发出!】");
                SKP = BIDP; //卖开委托价格设为买一价
                SKID = COD.A_SendOrder(Enum_Sell,Enum_Entry,SKM,SKP,TAC); //以卖开委托价格发出卖开委托手数的卖开委托
                SKFLG = 1; //已发出卖开委托
             }
             Else If(SKM == 0) //如果卖开委托手数等于0
             {
                Commentary("【" + COD + "做空:卖开委托完成!】");
                Exit(); //退出公式
             }
          }
       }
       Else If(KPFLG == 3 || KPFLG == 4) //如果已开启买平委托处理
       {
          If(BPFLG0 == 0 && BPFLG1 == 0 && BPFLG == 0) //如果没有买平委托
          {
             BPM = Min(ASKV,TVL); //买平委托手数
             If(BPM > 0) //如果买平委托手数大于0
             {
                If(SRP > 0) //如果有空头可用持仓
                {
                   If(SH == 1) //如果当前合约是上海市场合约
                   {
                      If(SRP0 > 0) //如果有空头今仓可用持仓
                      {
                         BPM0 = Min(SRP0,BPM); //买平今仓委托手数
                         BPM1 = Min(SRP1,BPM - BPM0); //买平老仓委托手数
                         Commentary("【" + COD + "平空:公式买平今仓" + Text(BPM0) + "手!】");
                         Commentary("【" + COD + "平空:买平今仓委托发出!】");
                         BPP0 = ASKP; //买平今仓委托价格设为卖一价
                         BPID0 = COD.A_SendOrder(Enum_Buy,Enum_ExitToday,BPM0,BPP0,TAC); //以买平今仓委托价格发出买平今仓委托手数的买平今仓委托
                         BPFLG0 = 1; //已发出买平今仓委托
                      }
                      Else If(SRP0 == 0) //如果没有空头今仓可用持仓
                      {
                         BPM1 = Min(SRP1,BPM); //买平老仓委托手数
                      }
                      If(KPFLG == 3 && SRP1 > 0 && BPM1 > 0) //如果未开启买平今仓委托处理,且有空头老仓可用持仓,且买平老仓委托手数大于0
                      {
                         Commentary("【" + COD + "平空:公式买平老仓" + Text(BPM1) + "手!】");
                         Commentary("【" + COD + "平空:买平老仓委托发出!】");
                         BPP1 = ASKP; //买平老仓委托价格设为卖一价
                         BPID1 = COD.A_SendOrder(Enum_Buy,Enum_Exit,BPM1,BPP1,TAC); //以买平老仓委托价格发出买平老仓委托手数的买平老仓委托
                         BPFLG1 = 1; //已发出买平老仓委托
                      }
                   }
                   Else //如果当前合约非上海市场合约
                   {
                      BPM = Min(SRP,BPM); //买平委托手数
                      Commentary("【" + COD + "平空:公式买平" + Text(BPM) + "手!】");
                      Commentary("【" + COD + "平空:买平委托发出!】");
                      BPP = ASKP; //买平委托价格设为卖一价
                      BPID = COD.A_SendOrder(Enum_Buy,Enum_Exit,BPM,BPP,TAC); //以买平委托价格发出买平委托手数的买平委托
                      BPFLG = 1; //已发出买平委托
                   }
                }
                Else If(SRP == 0) //如果没有空头可用持仓
                {
                   Commentary("【" + COD + "平空:买平委托完成!】");
                   Exit(); //退出公式
                }
             }
             Else If(BPM == 0) //如果买平委托手数等于0
             {
                Commentary("【" + COD + "平空:买平委托完成!】");
                Exit(); //退出公式
             }
          }
       }
       Else If(KPFLG == 5 || KPFLG == 6) //如果已开启卖平委托处理
       {
          If(SPFLG0 == 0 && SPFLG1 == 0 && SPFLG == 0) //如果没有卖平委托
          {
             SPM = Min(BIDV,TVL); //卖平委托手数
             If(SPM > 0) //如果卖平委托手数大于0
             {
                If(BRP > 0) //如果有多头可用持仓
                {
                   If(SH == 1) //如果当前合约是上海市场合约
                   {
                      If(BRP0 > 0) //如果有多头今仓可用持仓
                      {
                         SPM0 = Min(BRP0,SPM); //卖平今仓委托手数
                         SPM1 = Min(BRP1,SPM - SPM0); //卖平老仓委托手数
                         Commentary("【" + COD + "平多:公式卖平今仓" + Text(SPM0) + "手!】");
                         Commentary("【" + COD + "平多:卖平今仓委托发出!】");
                         SPP0 = BIDP; //卖平今仓委托价格设为买一价
                         SPID0 = COD.A_SendOrder(Enum_Sell,Enum_ExitToday,SPM0,SPP0,TAC); //以卖平今仓委托价格发出卖平今仓委托手数的卖平今仓委托
                         SPFLG0 = 1; //已发出卖平今仓委托
                      }
                      Else If(BRP0 == 0) //如果没有多头今仓可用持仓
                      {
                         SPM1 = Min(BRP1,SPM); //卖平老仓委托手数
                      }
                      If(KPFLG == 5 && BRP1 > 0 && SPM1 > 0) //如果未开启卖平今仓委托处理,且有多头老仓可用持仓,且卖平老仓委托手数大于0
                      {
                         Commentary("【" + COD + "平多:公式卖平老仓" + Text(SPM1) + "手!】");
                         Commentary("【" + COD + "平多:卖平老仓委托发出!】");
                         SPP1 = BIDP; //卖平老仓委托价格设为买一价
                         SPID1 = COD.A_SendOrder(Enum_Sell,Enum_Exit,SPM1,SPP1,TAC); //以卖平老仓委托价格发出卖平老仓委托手数的卖平老仓委托
                         SPFLG1 = 1; //已发出卖平老仓委托
                      }
                   }
                   Else //如果当前合约非上海市场合约
                   {
                      SPM = Min(BRP,SPM); //卖平委托手数
                      Commentary("【" + COD + "平多:公式卖平" + Text(SPM) + "手!】");
                      Commentary("【" + COD + "平多:卖平委托发出!】");
                      SPP = BIDP; //卖平委托价格设为买一价
                      SPID = COD.A_SendOrder(Enum_Sell,Enum_Exit,SPM,SPP,TAC); //以卖平委托价格发出卖平委托手数的卖平委托
                      SPFLG = 1; //已发出卖平委托
                   }
                }
                Else If(BRP == 0) //如果没有多头可用持仓
                {
                   Commentary("【" + COD + "平多:卖平委托完成!】");
                   Exit(); //退出公式
                }
             }
             Else If(SPM == 0) //如果卖平委托手数等于0
             {
                Commentary("【" + COD + "平多:卖平委托完成!】");
                Exit(); //退出公式
             }
          }
       }
    }
End


通过快捷键F12或软件右上方菜单[账户]->期货账户->算法交易下单,调出算法交易下单界面,按下图所示方法实现手动下单调用算法交易:


注:①如果再次下单,会再次加载该模型,不会直接在前一个算法交易模型中继续执行。

②如果模型中含有函数Exit(),则模型执行完毕后会自动退出。


案例二:追价算法交易模型确保成交

模型满足开仓条件发出委托信号后,因行情变动快,加之下单手数多等原因,可能无法快速成交。通过加载追价算法交易模型,可及时将没有成交的部分进行自动撤单,然后自动按照编写的价格方式重新发出委托,如果出现价差过大或长时间无法成交等情况则放弃本次交易。源码如下:


//追价示例
Data
    data0:"m1709";
Vars
    Numeric N;//下单手数
    Numeric T; //时间间隔
    Numeric KC; //开仓条件
    Numeric NOW; //当前时间
    Global_Numeric BKID; //买开委托
    Global_Numeric BKFLG; //买开标志
    Global_Numeric BKDEL; //买开撤单标志
    Global_Numeric BKM; //买开委托手数
    Global_Numeric BKT; //买开委托发出时间
Begin
    //----------------------变量赋值--------------------//
    N = 2; //下单手数
    T = 3; //时间间隔
    KC = 1; //开仓条件设为1
    NOW = CurrentTime(); //当前时间
    //----------------------成交判断--------------------//
    If(BKFLG == 1) //如果有买开委托
    {
       If(F_OrderStatus(BKID) == Enum_Filled) //如果买开委托成交
       {
          Commentary("【做多:买开委托成交!】");
          BKFLG = 0; //买开标志归0
          BKDEL = 0; //买开撤单标志归0
       }
       Else If(F_OrderStatus(BKID) == Enum_Canceled) //如果买开委托被撤单
       {
          If(F_OrderFilledLot(BKID) > 0) //如果买开委托部分成交
          {
             BKM = BKM - F_OrderFilledLot(BKID); //买开委托手数自减成交手数
          }
          If(BKM > 0) //如果买开委托手数大于0
          {
             Commentary("【做多:买开委托追价!】");
             BKID = A_SendOrder(Enum_Buy,Enum_Entry,BKM,Price("Ask1")); //以对价发出买开委托手数的买开委托
             BKT = NOW; //买开委托发出时间设为当前时间
          }
          BKDEL = 0; //买开撤单标志归0
       }
       Else If(F_OrderStatus(BKID) == Enum_Declared || F_OrderStatus(BKID) == Enum_FillPart) //如果买开委托全部挂单或部分成交
       {
          If(TimeDiff(BKT,NOW) > T && BKDEL == 0) //如果时间间隔T秒,且未撤单
          {
             Commentary("【做多:买开委托撤单!】");
             F_DeleteOrder(F_OrderContractNo(BKID)); //撤掉买开委托挂单
             BKDEL = 1; //已发出撤掉买开委托挂单
          }
       }
    }
    //----------------------委托处理--------------------//
    If(BKFLG == 0) //如果没有买开委托
    {
       If(KC == 1) //如果满足开仓条件
       {
          Commentary("【做多:买开委托发出!】");
          BKM = N; //买开委托手数设为N手
          BKID = A_SendOrder(Enum_Buy,Enum_Entry,BKM,Price("Bid1")); //以排队价发出买开委托手数的买开委托
          BKT = NOW; //买开委托发出时间设为当前时间
          BKFLG = 1; //已发出买开委托
       }
    }
End


算法交易模型执行效果如下图所示,在第一次委托没有成交时,算法交易模型对委托进行了撤单,撤单后重新追价,确保了委托的成交。



案例三:分批下单,实现大单拆分

大客户由于资金量大,满足模型条件时如果一次下单手数过多很可能无法成交或直接将价格打穿。通过加载自己编写的分批算法交易模型,可在下单时由系统自动拆分成合适的小单分批委托,也可由算法交易模型监测价差过大时是否放弃后续委托。源码如下:


//大单拆分
Data
     data0:"m1709";
Vars
     Numeric n;//大单手数
     Numeric Pn;//分批数量
     Global_Numeric YN;//已委托数量
Global_Numeric PP;//第一笔开仓价格
     Numeric 开仓条件;
Begin
     n=20;
     Pn=5;
    If(YN<n && YN<>0)//如果不是第一笔开仓,并且开仓数没有完成
    {
       If(data0.Price("New")<=PP)
{
          //如果最新价小于等于第一笔开仓价,继续开仓
A_SendOrder(Enum_Buy,Enum_Entry,min(Pn,n-YN),data0.Price("bid1"));
YN = YN+min(Pn,n-YN);//记录已开仓数量
}
    }
    Else If(YN==0)
  {
//第一笔开仓,记录开仓价,用于判断价格没有往不利方向发展
A_SendOrder(Enum_Buy,Enum_Entry,Pn,data0.Price("bid1"));
           PP=data0.Price("bid1");//记录第一笔开仓价
YN = Pn;
  }  
End


算法交易模型执行效果如下图所示,利用算法交易模型,将20手委托拆分成4批委托,每批5手。



案例四:实现自己的止盈止损策略

软件中会提供几种默认止盈止损策略,但有时还是无法完全满足用户的多样性需要,算法交易模型的函数可取到盘口价格和持仓合约的信息(持仓价格、数量、和盈亏等),通过编写算法交易模型可定制属于您自己的止盈止损策略。

跟踪止盈是软件默认提供的策略,但如果希望在盈利达到一定值时再启动跟踪止盈策略,就无法实现了。那么在开仓后,算法交易模型就会开始取盘口价格和持仓价格实时监测,满足了条件会自动发平仓委托。源码如下:


//止盈止损
Data
    data0:"m1709";
Vars
    Global_Numeric N;//下单手数
    Global_Numeric M1; //价位倍数1
    Global_Numeric M2; //价位倍数2
    Global_Numeric M3; //价位倍数3
    Global_Numeric BID; //买一价
    Global_Numeric ASK; //卖一价
    Global_Numeric NEWP; //最新价
    Global_Numeric MINP; //最小变动价位
    Global_Numeric BKC; //做多条件
    Global_Numeric BKID; //买开委托
    Global_Numeric BKFLG; //买开标志
    Global_Numeric BKAP; //买开委托成交均价
    Global_Numeric BHP; //多头最高价
    Global_Numeric SPID; //卖平委托
    Global_Numeric SPFLG; //卖平标志
    Global_Numeric BRP; //多头可用持仓
    Global_Numeric BZYFLG; //多头止盈标志
Begin
    N = 2; //下单手数
    M1 = 3; //价位倍数1
    M2 = 1; //价位倍数2
    M3 = 3; //价位倍数3
    BKC = 1; //做多条件
    BID = Price("Bid1"); //买一价
    ASK = Price("Ask1"); //卖一价
    NEWP = Price("New"); //最新价
    MINP = Price("MinPrice");//最小变动价位
    BRP = F_BuyRemainPosition(); //多头可用持仓

   if(A_IsExchangeOpen() == 1) //如果当前状态是开盘
    {
       //----------------------成交判断--------------------//

      if(BKFLG == 1) //如果有买开委托
       {
          if(F_OrderStatus(BKID) == Enum_Filled) //如果买开委托成交
          {
             Commentary("【多头开仓:买开委托成交!】");
             BKAP = F_OrderFilledPrice(BKID); //买开委托成交均价
             BKFLG = 2; //买开委托已成交
          }
       }

      if(SPFLG > 0) //如果有卖平委托
       {
          if(F_OrderStatus(SPID) == Enum_Filled) //如果卖平委托成交
          {
             if(SPFLG == 1) //如果是多头止盈卖平委托
             {
                Commentary("【多头止盈:卖平委托成交!】");
             }
             else if(SPFLG == 2) //如果是多头止损卖平委托
             {
                Commentary("【多头止损:卖平委托成交!】");
             }
             BKFLG = 0; //买开标志归0
             SPFLG = 0; //卖平标志归0
             BZYFLG = 0; //多头止盈标志归0
          }
       }

      //----------------------委托处理--------------------//

      if(BKFLG == 0) //如果没有买开委托
       {
          if(BKC == 1) //如果满足做多条件
          {
             Commentary("【多头开仓:买开委托发出!】");
             BKID = A_SendOrder(Enum_Buy,Enum_Entry,N,ASK); //以对价发出N手的买开委托
             BKFLG = 1; //已发出买开委托
          }
       }

      if(BKFLG == 2 && SPFLG == 0) //如果买开委托已成交,且没有卖平委托
       {
          if(BZYFLG == 0) //如果多头止盈未开启
          {
             if(NEWP >= BKAP + M1 * MINP) //如果最新价大于等于买开委托成交均价加M1个价位
             {
                Commentary("【多头止盈:多头止盈开启!】");
                BHP = NEWP; //多头最高价设为最新价
                BZYFLG = 1; //多头止盈开启
             }
          }
          else if(BZYFLG == 1) //如果多头止盈已开启
          {
             BHP = max(NEWP,BHP); //多头最高价
             if(NEWP <= BHP - M2 * MINP) //如果最新价小于等于多头最高价减M2个价位
             {
                if(BRP > 0) //如果有多头可用持仓
                {
                   Commentary("【多头止盈:卖平委托发出!】");
                   SPID = A_SendOrder(Enum_Sell,Enum_Exit,BRP,BID); //以对价发出多头可用持仓手数的卖平委托
                   SPFLG = 1; //已发出多头止盈卖平委托
                   BZYFLG = 2; //多头止盈已卖平
                }
             }
          }
          if(BZYFLG != 2) //如果多头止盈未卖平
          {
             if(NEWP <= BKAP - M3 * MINP) //如果最新价小于等于买开委托成交均价减M3个价位
             {
                Commentary("【多头止损:卖平委托发出!】");
                SPID = A_SendOrder(Enum_Sell,Enum_Exit,BRP,BID); //以对价发出多头可用持仓手数的卖平委托
                SPFLG = 2; //已发出多头止损卖平委托
             }
          }
       }
    }
End


算法交易模型执行效果如下图所示,开仓之后满足止损平仓条件,会自动发出平仓委托:



前言

  从整个市场套利交易来看,仅依靠人工是无法满足机构投资者对套利交易需求的。首先,价差机会不会实时存在,特别在套利空间不明显后,手动下单的滑点、误差等交易成本会让实际锁定的利润偏差很多,甚至由预期的盈利变为亏损;其次一个成熟专业的套利系统,不仅需要实时监控交易机会、还要需要实时管理资金和风险,这些都涉及到大量的运算并需要运用金融工程的手段,肉眼看或心算是无法完成的。特别对于统计套利等套利模式,更是人工根本无法完成的。

  专业的套利交易需要依托于专业的交易平台和管理工具。可以将监控命令编制成计算机程序使命,由计算机完成实时监控,同时利用专业并强大的金融数据分析功能,完成样本篮子调整、冲击成本处理、资金管理、头寸控制、下单精细控制等复杂的金融工程计算。

优势

  • 套利K线图带有上下影线,做套利像做单合约一样
  • 支持内外盘套利交易,以及自由配比的套利合约交易
  • 支持套利程序化,支持资金管理、下单精细控制等策略编写

案例

案例:套利下单精细控制示例

1、了解套利滑点和套利“瘸腿”成交的产生

在我们整个交易的过程中,伴随着行情急速上升或者回落过程等情况下,会有很多不可控因素导致我们的交易模型出现滑点或者出现更严重的“瘸腿”成交,继而影响收益并伴随着更高的交易风险,这对我们的交易收益和成本控制是有很大影响的,也有可能会使得一个赚钱的模型最后变得效果不理想。而在震荡行情中,信号不稳定也是容易在无形之中增加了我们的交易难度。


2、交易滑点对策略和收益的影响

成交滑点和“瘸腿”成交:比如当跨期套利主力和次主力合约进行日内套利交易时发现,虽然套利机会较多,但是次主力成交不及主力成交活跃,盈利机会转瞬即逝,当目标价差出现时,可能会伴随着成交问题,导致滑点或者是“瘸腿”成交。所以我们需要在目标价差内,做好交易稳定性的同时控制滑点和“瘸腿”,控制交易风险和交易屏障。


3、产生交易滑点案例:

以某合约跨期套利买卖价差为例:不活跃合约的流动性较低,买价和卖价的价差并不稳定,容易产生滑点,极端情况下甚至超过4跳以上,这使得回测的结果并不理想。在理想成交下,我们尚且需要预留滑点冲击成本,而在部分特殊情况下,还要增加额外的冲击。这使得策略模型的盈利能力大幅下降,并伴随高风险。

我们将大豆和锌的套利收益做了简单比对,将成交滑点放大到了4跳,默认成交的基本维持在2跳,每次成交我们需要承担6跳的冲击成本,回测结果见图1、图2。

考虑进去滑点的冲击,沪锌策略在后期是稳定亏损的,豆一策略也仅仅可以在后期勉强维持,图3中给出盈利下降速度更是以几何级数变化。

虽然设定情况只是滑点影响,不涉及“瘸腿”成交,但是对于收益的影响已经显现出来,如果有瘸腿情况出现,那无疑增加了交易的不可控性。



图1  豆一跨期套利权益,额外滑点4跳


图2  沪锌跨期套利权益,额外滑点4跳(2015年1月至5月18日)


图3  滑点和收益比较

以上图表说明交易模型是否获得理想的盈利效果关键之一,是滑点的控制,虽然在交易算法控制上做出限制之后,可能会丢失一部分交易机会,但是也降低了交易风险,从模型的长期收益上来看,是有帮助的。


4、根据以上思路形成策略如下

Data
  Data1:"m1709"; //第一腿合约
  Data2:"m1711"; //第二腿合约
Vars
//------------------------------定义普通变量------------------------------
  Numeric Data1Ma; //第一腿合约近20笔TICK均值
  Numeric Data2Ma; //第二腿合约近20笔TICK均值
  Numeric Data1Fc; //第一腿合约近20笔TICK均值
  Numeric Data2Fc; //第二腿合约近20笔TICK均值
  Numeric Ma5; //5周期均线
  Numeric Ma10; //10周期均线
  Numeric Data1DownLine; //第一腿合约下轨
  Numeric Data2DownLine; //第二腿合约下轨
  Numeric Data1UpLine; //第一腿合约上轨
  Numeric Data2UpLine; //第二腿合约上轨
  Numeric Size(20); //Tick区大小
  Numeric i; //For循环变量                                     
  Numeric j; //For循环变量
//------------------------------定义数据区变量------------------------------
  Var_TickData Datum1; //第一腿合约数据区
  Var_TickData Datum2; //第二腿合约数据区
  Var_TickData Datum3; //第二腿合约数据区
//------------------------------定义条件变量------------------------------
  Numeric Data2SumBidVol; //第二腿合约最近3笔的买量和
  Numeric Data2SumAskVol; //第二腿合约最近3笔的卖量和
  Numeric Data2BidVol; //第二腿合约买量
  Numeric Data2AskVol; //第二腿合约卖量
  Numeric Data1Var; //第一腿合约标准差
  Numeric Data2Var; //第二腿合约标准差
  Numeric Data1Cond; //第一腿合约条件
  Numeric Data2Cond; //第二腿合约条件
//------------------------------定义委托标识全局变量------------------------------
  Numeric Data2SellID1;                     //第二腿合约卖出标识1
  Numeric Data2SellID2;                     //第二腿合约卖出标识2
  Numeric Data2SellID3;                     //第二腿合约卖出标识3
  Numeric Data2SellID4;                     //第二腿合约卖出标识4
  Numeric Data1SellID5;                     //第一腿合约卖出标识5
  Numeric Data1SellID6;                     //第一腿合约卖出标识6
  Numeric Data1BuyID1;                     //第一腿合约买入标识1
  Numeric Data1BuyID2;                     //第一腿合约买入标识2
  Numeric Data2BuyID3;                     //第二腿合约买入标识3

//------------------------------定义委托状态全局变量------------------------------
  Numeric Data2SellStatus1;                //第二腿合约卖出委托标识1的委托状态
  Numeric Data2SellStatus2;                //第二腿合约卖出委托标识2的委托状态
  Numeric Data2SellStatus3;                //第二腿合约卖出委托标识3的委托状态
  Numeric Data2SellStatus4;                //第二腿合约卖出委托标识4的委托状态
  Numeric Data1SellStatus5;                //第一腿合约卖出委托标识5的委托状态
  Numeric Data1SellStatus6;                //第一腿合约卖出委托标识6的委托状态
  Numeric Data1BuyStatus1;                //第一腿合约买入委托标识1的委托状态
  Numeric Data1BuyStatus2;                //第一腿合约买入委托标识2的委托状态
  Numeric Data2BuyStatus3;                //第二腿合约买入委托标识3的委托状态
//------------------------------公式关键字------------------------------
Setting
  SignalNoTrading:1;//模型发出信号不委托
  //------------------------------模型主体------------------------------
Begin
  if(Ref(Cross(Ma5,Ma10),1))//一个周期前MA5金叉MA10
  {
Buy;//买入
  }
  if(Ref(CrossDown(Ma5,Ma10),1))//一个周期前MA5死叉MA10
  {
Sell;//卖出
  }

//------------------------------取委托状态------------------------------
  Data2SellStatus1 = F_OrderStatus(GetGlobalVar2("Data2SellID1")) == Enum_Filled;//第二腿合约卖出委托标识1的委托状态为全部成交
  Data2SellStatus2 = F_OrderStatus(GetGlobalVar2("Data2SellID2")) == Enum_Filled;//第二腿合约卖出委托标识2的委托状态为全部成交
  Data2SellStatus3 = F_OrderStatus(GetGlobalVar2("Data2SellID3")) == Enum_Filled;//第二腿合约卖出委托标识3的委托状态为全部成交
  Data2SellStatus4 = F_OrderStatus(GetGlobalVar2("Data2SellID4")) == Enum_Filled;//第二腿合约卖出委托标识4的委托状态为全部成交
  Data2BuyStatus3 = F_OrderStatus(GetGlobalVar2("Data2BuyID3")) == Enum_Filled;//第二腿合约买入委托标识3的委托状态为全部成交
  Data1SellStatus5 = F_OrderStatus(GetGlobalVar2("Data1SellID6")) == Enum_Filled;//第一腿合约卖出委托标识5的委托状态为全部成交
  Data1SellStatus6 = F_OrderStatus(GetGlobalVar2("Data1SellID6")) == Enum_Filled;//第一腿合约卖出委托标识6的委托状态为全部成交
  Data1BuyStatus1 = F_OrderStatus(GetGlobalVar2("Data1BuyID1")) == Enum_Filled;//第一腿合约买入委托标识1的委托状态为全部成交
  Data1BuyStatus2 = F_OrderStatus(GetGlobalVar2("Data1BuyID2")) == Enum_Filled;//第一腿合约买入委托标识2的委托状态为全部成交

 

  Ma5 = Ma(Close,5);//5周期均线
  Ma10 = Ma(Close,10);//10周期均线
  PlotNumeric("Ma5",Ma5);
  PlotNumeric("Ma10",Ma10);
  Datum1 = Def_TickData("m1709",1,20);//第一腿合约取20笔TICK
  Datum2 = Def_TickData("m1711",1,20);//第二腿合约取20笔TICK
  Datum3 = Def_TickData("m1711",0,3);//第二腿合约取3秒的TICK
  If(Datum1.State == 1)
  {
For i = 0 To Size - 1  
{
  Data1Ma = (Datum1[i].TickPrice + Data1Ma) / 20;//计算第一腿合约的20笔TICK算数平均值
  Data1fc = Square(Datum1[i].TickPrice - Data1Ma) + Data2fc;
}
  }
  If(Datum2.State == 1)
  {
For j = 0 To Size - 1
{
  Data2Ma = (Datum2[j].TickPrice + Data2Ma) / 20;//计算第一腿合约的20笔TICK算数平均值
  Data2fc = Square(Datum2[j].TickPrice - Data2Ma) + Data2fc;
}
  }

 

  Data1Var = Sqrt(Data1fc/20);//计算第一腿合约标准差
  Data1UpLine = Data1Ma + 1.645 * Data1Var;//计算第一腿合约上轨
  Data1DownLine = Data1Ma - 1.645 * Data1Var;//计算第一腿合约下轨

  Data2Var = Sqrt(Data2fc/20);//计算第二腿合约标准差
  Data2BidVol = Data2.Price("BidVol");//第二腿合约买量
  Data2AskVol = Data2.Price("AskVol");//第二腿合约卖量
  Data2SumBidVol = Datum3[0].BidVol1 + Datum3[1].BidVol1 + Datum3[2].BidVol1;//第二腿合约最近3秒的买量和
  Data2SumAskVol = Datum3[0].AskVol1 + Datum3[1].AskVol1 + Datum3[2].AskVol1;//第二腿合约最近3秒的卖量和
  Data2UpLine = Data2Ma + 1.645 * Data2Var;//计算第二腿合约上轨
  Data2DownLine = Data2Ma - 1.645 * Data2Var;//计算第二腿合约下轨

  Data1Cond = Data1.Price("New") > Data1DownLine && Data1.Price("New") < Data1UpLine;//第一腿合约最新价大于20笔TICK均值且第一腿合约最新价小于上轨
  Data2Cond = Data2.Price("New") > Data2DownLine && Data2.Price("New") < Data2UpLine;//第二腿合约最新价大于20笔TICK均值且第二腿合约最新价小于上轨

//------------------------------定义开仓------------------------------
//------------------------------第二腿合约先开------------------------------
  If(Data1Cond == 1  &&  Data2Cond == 1  &&  Data2BidVol >=  Data2AskVol / 3  &&  Data2SumBidVol < Data2SumAskVol * 2  &&  Datum3[0].BidVol1 >=  Datum3[2].BidVol1 * 0.5)//当满足括号中条件时
  {
If(F_CurrentSig == 204 && GetGlobalVar2("BuyTimeCoin") == 0)//当前信号为BUY 且全局变量BuyTimeCoin没有存入任何值或值为0
{
  SetGlobalVar2("BuyTimeCoin",CurrentTime);//记录当前时间
  SetGlobalVar2("SCoin",0);//SCoin归零
}
If(F_CurrentSig == 204 && GetGlobalVar2("BuyTimeCoin") > 0)//信号为Buy信号且全局变量BuyTimeCoin已存入时间
{
  If(Data1.F_GetOpenOrderCount() == 0 && Data2.F_GetOpenOrderCount() == 0 && GetGlobalVar2("BCoin") == 0 && TimeDiff(GetGlobalVar2("BuyTimeCoin"),CurrentTime) >= 3)//持仓为0,且且两腿合约均无挂单 且信号发出时间与当前时间差值大于等于3秒时 Bcoin值为0
  {
If(Data2.F_SellRemainPosition() == 0 && Data1.F_BuyRemainPosition() == 0 && GetGlobalVar2("BCoin1") == 0)
{
  Data2SellID1 = Data2.A_SendOrder(Enum_Sell,Enum_Entry,2,Data2.Price("Bid1"));//对价卖开2手第二腿合约
  SetGlobalVar2("Data2SellID1",Data2SellID1);//存入委托标识Data2SellID1
  SetGlobalVar2("BCoin1",1);
}
If(Data2.F_SellRemainPosition() == 2 &&  Data2SellStatus1 == 1)//第二腿合约空头可用持仓为2手 且Data2SellStatus1的委托状态为全部成交
{
  Data2SellID2 = Data2.A_SendOrder(Enum_Sell,Enum_Entry,2,Data2.Price("Bid1"));//对价卖开2手第二腿合约
  SetGlobalVar2("Data2SellID2",Data2SellID2);//存入委托标识Data2SellID2
}
If(Data2.F_SellRemainPosition() == 4 &&  Data2SellStatus1 == 1 && Data2SellStatus2 == 1)//第二腿合约空头可用持仓为4手且Data2SellStatus1、Data2SellStatus2的委托状态为全部成交
{
  Data2SellID3 = Data2.A_SendOrder(Enum_Sell,Enum_Entry,3,Data2.Price("Bid1"));//对价卖开3手第二腿合约
  SetGlobalVar2("Data2SellID3",Data2SellID3);//存入委托标识Data2SellID3
}
If(Data2.F_SellRemainPosition() == 7 &&  Data2SellStatus1 == 1 && Data2SellStatus2 == 1 && Data2SellStatus3 == 1)//第二腿合约空头可用持仓为4手且Data2SellStatus1、Data2SellStatus2、Data2SellStatus3的委托状态为全部成交
{
  Data2SellID4 = Data2.A_SendOrder(Enum_Sell,Enum_Entry,3,Data2.Price("Bid1"));//对价卖开3手第二腿合约
  SetGlobalVar2("Data2SellID4",Data2SellID4);//存入委托标识Data2SellID4
  SetGlobalVar2("SellPrice",Data2.Price("New"));//存入此时的最新价
  SetGlobalVar2("BCoin",1);//将全局变量BCoin赋值为1
}
SetGlobalVar2("Time",CurrentTime);//存入当前时间
  }
}

//------------------------------撤单------------------------------
If((Data2SellStatus1 == 0 || Data2SellStatus2 == 0 || Data2SellStatus3 == 0 || Data2SellStatus4 == 0) && (Data2.Price("New") > GetGlobalVar2("SellPrice") + 5 * Data2.Price("MinPrice") || TimeDiff(GetGlobalVar2("Time"),CurrentTime) > 10))
//第二腿合约的4笔卖出开仓委托全部发出,且其中存在任意一笔委托没有全部成交且(第二腿合约当前最新价大于第四笔委托发出时的最新价5个最小变动价位以上或第4笔委托发出至当前时间大于10秒)
{
  Data2.F_DeleteOrder();//撤掉第二腿合约的全部挂单
  SetGlobalVar2("BCoin",0);//将全局变量Coin重新归0
  SetGlobalVar2("BCoin1",0);
}
//------------------------------第二腿合约成交后委托第一腿合约------------------------------
If(Data2.F_SellRemainPosition() == 10 && Data1.F_BuyRemainPosition() != 10 && Data1.F_GetOpenOrderCount() == 0 && Data2.F_GetOpenOrderCount() == 0 && GetGlobalVar2("Coin") == 0)//第二腿合约可用持仓为10手,且两腿合约均无挂单,Coin值为0
{
  Data1BuyID1 = Data1.A_SendOrder(Enum_Buy,Enum_Entry,10,Data1.Price("New"));//最新价买开第一腿合约
  SetGlobalVar2("Data1BuyID1",Data1BuyID1);//存入委托标识Data1BuyID1
  SetGlobalVar2("BuyTime1",CurrentTime);//存入当前买入时间
  SetGlobalVar2("BuyPrice",Data1.Price("New"));//存入此时最新价
  SetGlobalVar2("Coin",1);

}
    If(Data1BuyStatus1 == 0 && TimeDiff(GetGlobalVar2("BuyTime1"),CurrentTime) > 3)//Data1BuyStatus1的委托状态为没有全部成交,且挂单时间超过3秒
{
  Data1.F_DeleteOrder(F_OrderContractNo(GetGlobalVar2("Data1BuyID1")));//撤掉Data1BuyID1的挂单
  Data1BuyID2 = Data1.A_SendOrder(Enum_Buy,Enum_Entry,10-Data1.F_BuyRemainPosition,Data1.Price("New"));//计算合约1可用持仓,最新价买入不足10手的部分
  SetGlobalVar2("Data1BuyID2",Data1BuyID2);//存入委托标识Data1BuyID2
  SetGlobalVar2("BuyTime2",CurrentTime);//存入当前买入时间
}
    If(Data1BuyStatus2 == 0 && TimeDiff(GetGlobalVar2("BuyTime2"),CurrentTime) > 2)//Data1BuyStatus2的委托状态没有全部成交,且挂单时间超过3秒
{
  Data1.F_DeleteOrder(F_OrderContractNo(GetGlobalVar2("Data1BuyID2")));//撤掉Data1BuyID2的挂单
  Data1.A_SendOrder(Enum_Buy,Enum_Entry,10 - Data1.F_BuyRemainPosition,Data1.Price("RiseLimit"));////计算合约1可用持仓,涨停价买入不足10手的部分
  SetGlobalVar2("Coin",0);
}
  }

//------------------------------定义平仓------------------------------
  If(F_CurrentSig == 203 )//信号为Sell信号
  {
if(Data1.F_BuyRemainPosition() == 10  &&  Data2.F_SellRemainPosition() == 10 && Data1.F_GetOpenOrderCount == 0 && Data2.F_GetOpenOrderCount == 0 && GetGlobalVar2("SCoin") == 0)//两腿合约各持仓10手,且两腿合约均无挂单,SCoin值为0
{
  Data2BuyID3 = Data2.A_SendOrder(Enum_Buy,Enum_Exit,10,Data2.Price("RiseLimit"));//涨停价买平10手第二腿合约
  SetGlobalVar2("Data2BuyID3",Data2BuyID3);//存入委托标识Data2BuyID3
}
if(Data2BuyStatus3 == 1 && Data1.F_BuyRemainPosition() == 10)//Data2BuyStatus3的委托状态为全部成交且第一腿合约可用持仓为10手
{
  Data1SellID5 = Data1.A_SendOrder(Enum_Sell,Enum_Exit,10,Data1.Price("New"));//最新价卖平10手第1腿合约
  SetGlobalVar2("Data1SellID5",Data1SellID5);//存入委托标识Data1SellID6
  SetGlobalVar2("SellTime5",CurrentTime);//存入当前时间
}
if(Data1SellStatus5 == 0 && TimeDiff(GetGlobalVar2("SellTime5"),CurrentTime > 3))//Data1SellStatus5的委托状态为没有全部成交,且挂单时间大于3秒
{
  Data1.F_DeleteOrder(F_OrderContractNo(GetGlobalVar2("Data1SellID5")));//撤掉Data1SellID5的挂单
  Data1SellID6 = Data1.A_SendOrder(Enum_Sell,Enum_Exit,Data1.F_BuyRemainPosition,Data1.Price("New"));//最新价卖平合约1全部可用持仓
  SetGlobalVar2("Data1SellID6",Data1SellID6);//存入委托标识Data1SellID6
  SetGlobalVar2("SellTime6",CurrentTime);//存入当前时间
}
if(Data1SellStatus6 == 0 && TimeDiff(GetGlobalVar2("SellTime6"),CurrentTime > 2))//Data1SellStatus6的委托状态为没有全部成交,且挂单时间大于2秒
{
  Data1.F_DeleteOrder(F_OrderContractNo(GetGlobalVar2("Data1SellID6")));//撤掉Data1SellID6的挂单
  Data1.A_SendOrder(Enum_Sell,Enum_Exit,Data1.F_BuyRemainPosition,Data1.Price("FallLimit"));//跌停价卖出合约1的全部可用持仓
  SetGlobalVar2("SCoin",1);//SCoin赋值为1
}
SetGlobalVar2("BuyTimeCoin",0);//BuyTimeCoin归零
SetGlobalVar2("BCoin",0);//BCoin归零
  }

 

End


5、策略运行

将策略模型加载到套利K线主图进行计算,而后加载到套利页面,用来判断标的物的价格趋势,寻找套利的入场时机。策略加载成功后,对应的策略模型会自动加载运行,无需手动加载。策略模型出现信号后会自动对套利合约进行套利下单。



前言

  作为低风险的投资形式,对冲交易可以实现资产的合理分配,将市场风险分散。但这种形式的对冲交易受限于市场行情。例如,当市场不活跃趋于平静的时候,依赖市场波动获利的对冲基金失去了获利途径,处境艰难。又比如,阿尔法套利利用指数期货与证券进行反向对冲,做多相对指数具有超额收益的证券,做空相应指数期货,虽然可以在实现回避系统风险同时的对冲收益。但当上证指数处于熊市时由于指数期货价格低于现货价格,期现价差为负,很难发现阿尔法套利的入场机会。

  利用对冲K线图发现交易机会;对冲k线图与常规k线图不同,常规k线图以价格为纵坐标,对冲k线图以两腿篮子合约的总收益之和为纵坐标,可理解为随着时间变化的一篮子合约总收益和的k线图,在交易时把总收益曲线作为行情曲线。当行情缺乏波动性或出于熊市时,利用软件提供的对冲交易图表功能,可以帮助在平缓或熊市中拨云见日,发现期现对冲机会。

优势

  • 自由设置一篮子对冲合约
  • 对冲图表-收益和的K线走势图
  • 对冲程序化-可做效果测试及自动下单

案例

案例一:静态对冲,多合约对冲,长期持有稳定盈利
案例二:拨云见日,发现熊市中的期现套利机会
案例三:动态对冲,单合约对冲,频繁交易多次获利
对冲下单及程序化-可做效果测试及自动下单
金融工程师支持:400-811-3366(工作日8:00~22:00,十一、春节17:00~22:00)
© 上海文华财经资讯股份有限公司   ICP证号:沪B2-20110035