2. 我们有了一个MA指标,现在利用它来实现一个双均线交叉交易策略吧!
3. ValueSeries 类的AddOrFlush()方法的错误用法:
如何写自己的交易策略
编写自己的交易策略需要一些c#知识,在此不多做介绍。List<T>等数据结构均可在策略中使用。
基本概念:
1. DateSeries(日期序列):
DateSeries 是一个日期类型的“数组”。
2. ValueSerie(值序列):
ValueSerie可以理解为:一个可绘制在窗口区的 double 类型的 “数组”。
值序列[0]表示序列中的倒数第一个值。
值序列[1]表示序列中的倒数第二个值。
……
值序列[n]表示序列中的倒数第 n + 1个值。
3. Indicator(指标):
Indicator是每收到一个行情数据时,都会按其实现逻辑自动计算的值序列。
4. ContractBarSeries(合约):
ContractBarSeries是包含 Open、High、Low、Close、Vol、OI值序列及合约信息的集合。
5. Strategy(策略):
Strategy 是每收到一个行情数据时,都会按其实现逻辑自动计算并发单的ContractBarSeries 的集合。
开始实践:
1.写一个指标
打开VS并新建一个项目—“类库(.NET Framework)”。选择“”菜单下的“配置管理”命令。在弹出的“配置管理器”对话框中,在“平台”的下拉列表框中选择“<新建...>”。那么“平台”下的选项会变为“x64”,单击“关闭”按钮关闭对话框。将“Base.dll”文件复制到程序目录中,并添加对其的引用。添加using TradePlat语句。
指标代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TradePlat;
namespace IndicatorLib
{
///<summary>
///计算简单移动平均 SMA,通过 R 属性返回计算结果
///</summary>
publicclassMA : Indicator
{
///<summary>
///
///</summary>
///<paramname="strategy 指标所属的策略"></param>
///<paramname="str 输入序列的名称。如:“Close”、“High”等"></param>
///<paramname="p 计算周期"></param>
public MA(ContractBarSeries contractBarSeries, string str) : base(contractBarSeries, str)
{
}
public MA(ValueSeries vs) : base(vs)
{
}
privateint _period;
privateValueSeries _outSeries = newValueSeries(0); //输出系列
///<summary>
///指标名--指标名 + 参数
///</summary>
publicoverridestring Name
{
get => "MA:" + Period;
}
///<summary>
///指标计算周期--小于零则返回 0
///</summary>
publicint Period
{
get => _period;
set
{
if (value < 0)
{
_period = 0;
}
else
{
_period = value;
}
}
}
//计算过程中使用的变量
double sum; //和
///<summary>
///计算并更新指标
///当指标计算参数或输入系列长度小于等于零时,返回包含零个元素的 ValueSeries(0)
///</summary>
///<param name="inBar"></param>
publicoverridevoid UpData(bool inBar)
{
if (_period <= 0 || _inSeries.Length <= 0)
{
_outSeries = newValueSeries(0);
}
else
{
if (_inSeries.Length < _period)
{
_outSeries.AddOrFlush(double.NaN, inBar);
}
else
{
sum = 0;
for (int i = 0; i < _period; i++)
{
sum += _inSeries[i];
}
_outSeries.AddOrFlush(sum /_period, inBar);
}
}
}
///<summary>
///返回输出系列
///</summary>
publicoverrideValueSeries R
{
get => _outSeries;
}
}
}
2. 我们有了一个MA指标,现在利用它来实现一个双均线交叉交易策略吧!
打开VS并新建一个项目—“类库(.NET Framework)”。选择“”菜单下的“配置管理”命令。在弹出的“配置管理器”对话框中,在“平台”的下拉列表框中选择“<新建...>”。那么“平台”下的选项会变为“x64”,单击“关闭”按钮关闭对话框。将“Base.dll”文件及上一步生成的“IndicatorLib.dll”文件复制到程序目录中,并添加对他们的引用。添加usingSystem.ComponentModel; 、using TradePlat;和using IndicatorLib;语句。
策略代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.ComponentModel;
using TradePlat;
using IndicatorLib;
namespace StrategyLib
{
///<summary>
///
///</summary>
publicclassTwoSma : Strategy
{
privateContractBarSeries _bs;
privateMA ma;
privateMA ma2;
privateVolume vol;
[CategoryAttribute("参数"), DescriptionAttribute("短周期-多少个Bar")]
publicint Period1
{
get => ma.Period;
set
{
if (value < 0)
{
ma.Period = 0;
}
else
{
ma.Period = value;
}
}
}
[CategoryAttribute("参数"), DescriptionAttribute("长周期-多少个Bar")]
publicint Period2
{
get => ma2.Period;
set
{
if (value < 0)
{
ma2.Period = 0;
}
else
{
ma2.Period = value;
}
}
}
///<summary>
///
///</summary>
///<paramname="bs"></param>
///<paramname="str"></param>
///<paramname="p"></param>
public TwoSma(ContractBarSeries[] bsArray) : base(bsArray)
{
if (bsArray != null && bsArray.Length > 0)
{
_bs = bsArray[0];
AddToCurrentWindow(_bs);
ma = newMA(_bs, "Close");
AddToCurrentWindow(ma); //将指标 ma 添加到当前窗口区以显示
ma2 = newMA(_bs, "Close");
AddToCurrentWindow(ma2); //将指标 ma2 添加到当前窗口区以显示
//vol = newVolume(_bs);
//vol.R.IsPillar = true; //以差状图形式绘制成交量
//AddToNewWindow(vol); //将指标 vol 添加到新的窗口区以显示
}
}
///<summary>
///策略名称
///</summary>
publicoverridestring Name
{
get => this.GetType().ToString();
}
publicoverridestring KeyName
{
get => _bs.InstrumentName;
}
///<summary>
///策略参数的字符串表示
///</summary>
publicoverridestring ParameterString
{
get
{
return"Period1:" + Period1 + ", Period2:" + Period2 + ", " + base.ParameterString;
}
}
///<summary>
///返回序列--测试时用
///</summary>
[Browsable(false)]
publicValueSeries Return
{
get => ma.R;
}
///<summary>
///策略具体执行的内容
///</summary>
///<paramname="inBar"></param>
protectedoverridevoid Execute(bool inBar)
{
if (Period1 == 0 || Period2 == 0)
{
OnError("参数 Period 设置有误!");
}
else
{
ma.UpDataIndicator(inBar);
ma2.UpDataIndicator(inBar);
if (_bs.NoHasLong
&& (ma.R[2]<= ma2.R[2] && ma.R[1] > ma2.R[1]))//上穿
{
_bs.BuyToCover(_bs.Open[0],"s1", Lots);
_bs.Buy(_bs.Open[0], "b1", Lots);
}
if (_bs.NoHasShort
&& (ma.R[2] >=ma2.R[2] && ma.R[1] < ma2.R[1]))//下穿
{
_bs.Sell(_bs.Open[0], "b1", Lots);
_bs.SellShort(_bs.Open[0], "s1", Lots);
}
}
}
//
}
3. 怎样使用我们的策略呢?
将“IndicatorLib.dll”文件复制到程序目录下,将“StrategyLib.dll”文件复制到程序目录下的“strategies”文件夹下。现在运行我们的程序就可以在程序中加载刚刚实现的策略了。
应注意的问题:
1. 在实盘策略中,不要使用未来数据
像 _bs.High[0]、_bs.Low[0] 这样的数据我们称为:未来数据,它们表示当前这根 Bar 的最高价、最低价。由于这根 Bar 并未结束,所以它的最高价、最低价仍在变化之中,尚不确定。
由于 _bs.Open[0] 在这根一出现时,便已确定。所以它不是未来数据。_bs.Close[0] 则代表这根 Bar 的最新价,所以它也不是未来数据。
2.在实盘状态下,与未连接行情的模拟状态会有所不同:
if (_bs.Close[0] >= BuyPosition[0]) //在实盘状态下表示最新价 >= BuyPosition[0]
{
……
}
if(_bs.Close[0] >= BuyPosition[0]) //在静态状态下表示收盘价 >= BuyPosition[0]
{
……
}
由于这种情况的存在,所以在接入实盘数据前要求输入目前实际持仓的信息。
3.ValueSeries 类的AddOrFlush()方法的错误用法:
val = (_mPeriod *_inSeries[0] + (_nPeriod - _mPeriod) * _outSeries[0]) / _nPeriod;
_outSeries.AddOrFlush(val,inBar);
错误的原因:指标每 tick 会运行一次,上面代码是用 _outSeries[0] 更新 _outSeries[0],得到是当前Bar 的累积值。
正确的写法:
if (!inBar)
{
_outSeries.Add(double.NaN);
}
val = (_mPeriod *_inSeries[0] + (_nPeriod - _mPeriod) * _outSeries[1]) / _nPeriod;
_outSeries.Flush(val);