使用.NET搭建基于掘金平台的量化交易框架

    合规说明:本文章仅供学习交流使用,文中涉及到的投资策略、操作方式、提及的个股等等,均是技术性演示使用,仅供技术性学习参考,不构成投资建议,请大家遵守!

    前言:之前写过一部分量化开发文章,不过都是自个儿操作,或者借助QMT交易来操作。QMT量化交易只能使用Python开发,虽然可以利用C# 进行开发策略,然后通过python开发的miniqmt框架进行通信对接来间接交易,但是整体上还是感觉有点怪怪的,有种慢半拍的即视感。所以现在打算写一篇入门版本的,基于掘金平台,完全使用C# 进行开发的小例子。只做基础框架开发,供参考。

    首先你需要自己注册掘金量化平台的账号。注册地址:

https://www.myquant.cn/

    如果你是只做策略研究使用,就可以使用免费的基础版本

    如果你需要通过程序化进行交易,你需要先和你的券商客户经理了解,有没有和掘金量化平台合作,有合作的话,自行申请量化交易权限以后,再找掘金平台提供指定券商的股票版本进行使用,不然是没办法盘中程序化交易的。按照目前市场标准情况,QMT门槛是2万到500W不等;量化交易权限是5W到500W不等,根据你的券商情况而定(咨询你自己的客户经理)。

    接下来,我做个从头搭建一个量化框架的入门教程,供参考。先创建一个asp.net core webapi项目

SDK版本我本地直接使用.net 8

    掘金平台客户端上面,找到 我的策略->新建策略,使用C# 创建一个空策略

    创建完成,会生成一个策略ID,可以保存下来备用,后面做数据回测时候需要用到。

接着,还需要一个你的账户token,用于授权使用。右上角 系统设置内,找到密钥管理,这个密钥也需要用到,可以一起拿着备用。

在前面创建的项目上,可以添加一个策略文件夹,文件夹用于存储你自己的策略集合。例如第一个策略,也放这里面来。

    安装掘金SDK,我使用的64位,大家可以根据自己的系统来定。

    在新建的策略类里面,继承Strategy,并且补全构造函数,以及重写OnInit函数,这个步骤是必须的。然后就可以在OnInit里面进行策略的开发,可以做数据订阅、定时器等等。以下我就按照最简单的,做一个策略,每天下午14点55分执行一次判断,需要订阅数据推送,订阅的数据,需要在重写的OnTick函数里面接收,然后在尾盘55分时候执行判断是不是符合规则。如下图所示。

    接下来写一个简单的小策略,例如,10日线上穿20日线买入,下穿20日线卖出

    做策略回测,需要写一个对应的回测类,回测里面就需要你的策略ID和用户Token了,这样就可以根据测试结果,推送到你的掘金客户端上面展示

然后执行回测类的测试函数,在策略研究里面,就可以看到策略执行进度

    客户端的绩效分析里面,可以看到一些回测具体信息,以及你的历史BS点,这样就可以轻松看出你和指数的比较,以及你的策略是不是符合你的预期收益了。

继续换一个简单策略,例如股价第一次站上20日线买入,跌破卖出

策略不一样,同一个个股来说,一般执行的回测收益也会有差异

    以上只是演示量化回测的例子,并不具有投资建议属性,千万不要无脑直接拿去用。

上面测试的源码如下:

namespace WeskyQuant.Strategies{    /// <summary>    /// 第一个策略    /// </summary>    public class MyFirstStrategy : Strategy    {        public MyFirstStrategy(string token, string strategyId, StrategyMode mode) : base(token, strategyId, mode) { }

        /// <summary>        /// 重写OnInit事件,进行策略开发        /// </summary>        public override void OnInit()        {            Console.WriteLine("策略开始初始化");            Subscribe("SZSE.000001", "tick");            return;        }
        DateTime currentDate; // 当前时间
        /// <summary>        /// 接收掘金推送的数据        /// </summary>        /// <param name="tick"></param>        public override void OnTick(Tick tick)        {            if (tick.createdAt.Hour < 14)            {                return; // 14点之前,不操作            }            if (tick.createdAt.Hour == 14 && tick.createdAt.Minute < 55)            {                return; // 14点55分之前,不操作            }
            if (currentDate == tick.createdAt.Date)            {                return; // 今天已经处理过了,防止重复处理            }            currentDate = tick.createdAt.Date;
            GMDataList<Bar> historyBar = GMApi.HistoryBars(tick.symbol, "1d", tick.createdAt.AddDays(-15).ToString("yyyy-MM-dd HH:mm:ss"), tick.createdAt.ToString("yyyy-MM-dd HH:mm:ss"), Adjust.ADJUST_PREV);            if (historyBar == null)            {                Console.WriteLine("获取历史数据失败");                return;            }
            var ma10 = historyBar.data.TakeLast(10).Average(x => x.close);            var ma10_ref1 = historyBar.data.TakeLast(11).SkipLast(1).Average(x => x.close);            var ma20 = historyBar.data.TakeLast(20).Average(x => x.close);            var ma20_ref1 = historyBar.data.TakeLast(21).SkipLast(1).Average(x => x.close);
            var close = historyBar.data.Last().close;            var close_ref1 = historyBar.data.SkipLast(1).Last().close;
            if (close > ma20 && close_ref1<=ma20_ref1)            {                GMData<Order> o = OrderValue("SZSE.300127", 100000, OrderSide.OrderSide_Buy, OrderType.OrderType_Market, PositionEffect.PositionEffect_Open, 0);                if (o.status == 0)                {                    Console.WriteLine($"{tick.createdAt}:委托买入成功");                }                else                {                    Console.WriteLine("委托买入失败");                }            }            else if (close < ma20 && close_ref1 >= ma20_ref1)            {                GMData<Order> o = OrderValue("SZSE.300127", 100000, OrderSide.OrderSide_Sell, OrderType.OrderType_Market, PositionEffect.PositionEffect_Close, 0);                if (o.status == 0)                {                    Console.WriteLine($"{tick.createdAt}:委托卖出成功");                }                else                {                    Console.WriteLine("委托卖出失败");                }            }

            //if (ma10 > ma20)            //{            //    if (ma10_ref1 <= ma20_ref1)            //    {            //        // 10日上穿20日            //        GMData<Order> o = OrderValue("SZSE.300127", 100000, OrderSide.OrderSide_Buy, OrderType.OrderType_Market, PositionEffect.PositionEffect_Open, 0);            //        if (o.status == 0)              //        {            //            Console.WriteLine($"{tick.createdAt}:委托买入成功");            //        }            //        else            //        {            //            Console.WriteLine("委托买入失败");            //        }            //    }            //}            //else if (ma10 < ma20)            //{            //    if (ma10_ref1 >= ma20_ref1)            //    {            //        // 下穿            //        GMData<Order> o = OrderValue("SZSE.300127", 100000, OrderSide.OrderSide_Sell, OrderType.OrderType_Market, PositionEffect.PositionEffect_Close, 0);            //        if (o.status == 0)             //        {            //            Console.WriteLine($"{tick.createdAt}:委托卖出成功");            //        }            //        else            //        {            //            Console.WriteLine("委托卖出失败");            //        }            //    }            //}
        }
        /// <summary>        /// 第一个策略的回测函数        /// </summary>        public class MyFirstStrategyBackTest        {            public void Test()            {                //回测一年                var startTime = DateTime.Now.AddDays(-365).ToString("yyyy-MM-dd 8:20:00");                var endTime = DateTime.Now.AddDays(-1).ToString("yyyy-MM-dd 17:30:00");                MyFirstStrategy s = new MyFirstStrategy("你的token", "你的策略ID,每次新建策略,都会有一个唯一ID,你的回测数据会在策略ID对应的策略下面查看到", StrategyMode.MODE_BACKTEST);                s.SetBacktestConfig(startTime, endTime);                int nRet = s.Run();                if (nRet != 0)                {                    System.Console.WriteLine("回测失败, 错误码: {0}", nRet);                }                else                {                    System.Console.WriteLine("回测完成!");                }            }        }    }}
python+opencv简谱识别音频生成系统源码含GUI界面+详细运行教程+数据 一、项目简介 提取简谱中的音乐信息,依据识别到的信息生成midi文件。 Extract music information from musical scores and generate a midi file according to it. 二、项目运行环境 python=3.11.1 第三方库依赖 opencv-python=4.7.0.68 numpy=1.24.1 可以使用命令 pip install -r requirements.txt 来安装所需的第三方库。 三、项目运行步骤 3.1 命令行运行 运行main.py。 输入简谱路径:支持图片或文件夹,相对路径或绝对路径都可以。 输入简谱主音:它通常在第一页的左上角“1=”之后。 输入简谱速度:即每分钟拍数,同在左上角。 选择是否输出程序中间提示信息:请输入Y或N(不区分大小写,下同)。 选择匹配精度:请输入L或M或H,对应低/中/高精度,一般而言输入L即可。 选择使用的线程数:一般与CPU核数相同即可。虽然python的线程不是真正的多线程,但仍能起到加速作用。 估算字符上下间距:这与简谱中符号的密集程度有关,一般来说纵向符号越稀疏,这个值需要设置得越大,范围通常在1.0-2.5。 二值化算法:使用全局阈值则跳过该选项即可,或者也可输入OTSU、采用大津二值化算法。 设置全局阈值:如果上面选择全局阈值则需要手动设置全局阈值,对于.\test.txt中所提样例,使用全局阈值并在后面设置为160即可。 手动调整中间结果:若输入Y/y,则在识别简谱后会暂停代码,并生成一份txt文件,在其中展示识别结果,此时用户可以通过修改这份txt文件来更正识别结果。 如果选择文件夹的话,还可以选择所选文件夹中不需要识别的文件以排除干扰
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值