##开源量化交易系统:分享源码之我的低延时交易框架

先扯淡几句。。。

一个出身非211大学的我当前一毕业就投身于投资行业,从最开始的主观交易、手动报撤单到现在的策略化、量化的研究二级市场,回头一看,坎坎坷坷的走了不少弯路。

好了,废话不说,上重点!

☆福利☆ 交易系统整个项目的源码地址 ☞↓

链接:https://pan.baidu.com/s/12n-y33OgXaQt3HPN-3KC6w 
提取码:raxk

☆福利☆ 交易系统整个项目的源码地址 ☞↑

国内真正好的量化交易系统基本都是能藏多隐秘则藏多隐秘的,生怕别人复制了自己的套路,笔者有幸在私募和券商业内从业多年,多多少少的了解咯点量化交易系统的东西,系统吸收了私募基金使用的量化交易系统的特点,这使得本系统能非常适应市面的量化交易策略的开发(系统在策略和底层接口的包容性、低延时性上作了大量的工作,以保障交易员的正常使用)。

系统能完成包括套利、趋势等所有量化策略种类(股票与期货);系统能接入包括CTP、飞马、Fix和恒指、美股的接口,系统的独立模块封装形式使得系统能以最开放包容的姿态支持各种API和量化策略。

ps:2019.09.27日志更新:经过漫长的完善和周边开发以后,量化交易系统已经达到能推向市场(上实盘的能力),至此,笔者打算开源了这套系统(除了最核心的中层封装)

《一》、量化交易系统主界面展示

                                                                                                           (1)

上图1是交易界面部分,界面部分包含了委托回报、交易回报、持仓回报和账户信息回报。

《二》、量化交易系统策略管理模块UI展示:

 

                                   

                                                                                     (2)

上图2是启动策略组界面后读取到的本地所有策略信息,这些信息需要策略编写人员在写策略的时候继承BaseStrategy的字段重写。

《三》、量化交易系统策略参数模块UI展示:

                                                                             

                                                                                      (3)

上图3则是选择加载了策略后显示所在策略的所有参数信息,可以进行参数的修改以适应策略的变化。

 

至此,就可以通过编写一个个人的策略来实现量化程序化策略咯

《四》、完整的一个量化交易系统模板源码展示:

(↓↓以下是基于量化交易系统开发的一个T+0的工具策略demo源码↓↓)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using HotDancer.Common;
using HotDancer.Common.BaseMdApi;
using HotDancer.Common.BaseTdApi;
using System.IO;
using HotDancer.BaseStrategy;
using System.ComponentModel;

namespace T0_Rtn_Trade_stockdata
{
    public class T0_Rtn_Trade : BaseStrategy
    {
        [Hot(AnyName = "code1")]
        public string code1 = "i1909";

        public IBaseMdApi QuotaApiStock_deep;
        public IBaseTdApi TradeApi;
        public override string Author => "铁手罗汉拳";
        public override string UpdateTime => "2019-06-18";
        public override string StrategyLog => "T0策略辅助交易界面";
        public override string StrategyName => "T0终结者";
        public event Action<Dictionary<string, OrderReturnInforDataAny>> OnNotTradeQueeing;  //未成交队列推送
        public event Action<OnRspInvestorPositonDelailAny> OnPositionDelailField; //持仓明细事件
        public event Action<Dictionary<string, StockCodeInfo>> myCodeInfoDataEvent;  //声明事件
        public event Action<IBaseMdApi, OnQuoteMarketDataAny> onQuotaData;   //推送行情到界面
        public event Action<List<string>, List<StockCodeInfo>, List<StockMixPosition>> OnAllSubCodes;  //给窗体传送所有一定订阅的code
        //public event Action<BaseQuotaApi, TransData> onTranData;   //推送逐笔成交的行情到界面
        public List<string> allCodeList;// = new List<string>();
        /// <summary>
        /// 存放股票code对应的信息
        /// </summary>
        public Dictionary<string, StockCodeInfo> data_codeinfo = new Dictionary<string, StockCodeInfo>();
        [Description("策略的窗体")] Form1 form;
        public string codeListPath = @".\Config\SubCodes.csv";
        public string codeNameListPath = @".\Config\CodeNameList.csv";
        public string codeCountListPath = @".\Config\CodeToCount.csv";
        public string tabpageNotTradingPath = @".\Config\攻略.png";
        public string tabpagekKeepingPath = @".\Config\我的.png";
        public override void Start()
        {
            NotTradeQueeingDict = new Dictionary<string, OrderReturnInforDataAny>();
            AllTradedQueeingDict = new Dictionary<string, OrderReturnInforDataAny>();
            var codeList = GetCSVStockCodes(codeListPath);
            var codeNameList = GetCSVStockNameCodes(codeNameListPath);
            var codeCountList = GetCSVStockCountCodes(codeCountListPath);
            QuotaApiStock_deep = QuoteApis.Find(new Predicate<IBaseMdApi>((c) => { return c.ApiType == EnumQuoteApiTypeAny.CTP期货; }));

            QuotaApiStock_deep.OnQuoteData += QuotaApiStock_deep_OnQuoteData;
            QuotaApiStock_deep.OnRspLogin += QuotaApiStock_deep_OnRspLogin;
            QuotaApiStock_deep.QuoteApiDisConnected += QuotaApiStock_deep_QuoteApiDisConnected;

            TradeApi = TradeApis.Find(new Predicate<AccountLoginDataField<IBaseTdApi>>((c) => { return c.TExample.ApiType == EnumTradeApiTypeAny.CTP期货; })).TExample;
            TradeApi.OnRtnOrder += TradeApi_OnRtnOrder;
            TradeApi.OnRtnTrade += TradeApi_OnRtnTrade;
            TradeApi.OnRspOrderAction += TradeApi_OnRspOrderAction;
            TradeApi.OnRtnErrOrderAction += TradeApi_OnRtnErrOrderAction;
            TradeApi.OnRspQryInvestorDetailPosition += TradeApi_OnRspQryInvestorDetailPosition;
            allCodeList = new List<string>() { code1, "rb1910", "sc1908", "pb1907", "ag1909", "cf1909", "ma1909", "rm1909" };
            foreach (var item in allCodeList)
            {
                SubStockCode(item);
            }

            data_codeinfo = GetStockInfor(allCodeList);   //获取股票的中文名/昨收价

            form = new Form1(this);
            //传送所有SubCode
            OnAllSubCodes?.Invoke(QuotaApiStock_deep.AllSubMarketCodes, codeNameList, codeCountList);
            form.OnOrderInsertEvent += Form_OnOrderInsertEvent;
            form.Show();
        }
        /// <summary>
        /// 根据ErrorMsg 剔除已经被撤掉的未成交单
        /// </summary>
        /// <param name="msg"></param>
        private void HandleRemoveNotTradeQueedingDict(string msg,string orderRef)
        {
            if (msg.Contains("CTP:报单已全成交或已撤销,不能再撤"))
            {
                if (NotTradeQueeingDict.ContainsKey(orderRef))
                {
                    Log($"策略ten=>CTP:报单已全成交或已撤销,当前未成交单子:{orderRef}被队列remove");
                    NotTradeQueeingDict.Remove(orderRef);
                }
                OnNotTradeQueeing?.Invoke(NotTradeQueeingDict);
            }
        }

        /// <summary>
        /// 撤单失败回报
        /// </summary>
        /// <param name="arg1"></param>
        /// <param name="arg2"></param>
        /// <param name="arg3"></param>
        private void TradeApi_OnRtnErrOrderAction(IBaseTdApi arg1, OnRtnErrOrderActionField arg2, ErrorInfoAny arg3)
        {
            HandleRemoveNotTradeQueedingDict(arg3.ErrorMsg, arg2.OrderRef);
        }
        /// <summary>
        /// 撤单操作回报
        /// </summary>
        /// <param name="arg1"></param>
        /// <param name="arg2"></param>
        /// <param name="arg3"></param>
        /// <param name="arg4"></param>
        /// <param name="arg5"></param>
        private void TradeApi_OnRspOrderAction(IBaseTdApi arg1, OnRspInputOrderActionFieldAny arg2, ErrorInfoAny arg3, int arg4, bool arg5)
        {
            HandleRemoveNotTradeQueedingDict(arg3.ErrorMsg, arg2.OrderRef);
        }
        /// <summary>
        /// 持仓明细
        /// </summary>
        /// <param name="arg1"></param>
        /// <param name="arg2"></param>
        /// <param name="arg3"></param>
        /// <param name="arg4"></param>
        /// <param name="arg5"></param>
        private void TradeApi_OnRspQryInvestorDetailPosition(IBaseTdApi arg1, OnRspInvestorPositonDelailAny arg2, ErrorInfoAny arg3, int arg4, bool arg5)
        {
            OnPositionDelailField?.Invoke(arg2);
        }
        public Dictionary<string, OrderReturnInforDataAny> AllTradedQueeingDict;
        private void TradeApi_OnRtnTrade(IBaseTdApi arg1, TradeReturnInforDataAny arg2)
        {
            ;
        }
        /// <summary>
        /// 记录所有未成交的RtnOrder
        /// 1.从OnRtnOrder开始记录
        /// 2.一旦成交(全部、部分),则修改队列中的数据(全部成交则提出队列,部分成交则修改未成交数量)
        /// </summary>
        public Dictionary<string, OrderReturnInforDataAny> NotTradeQueeingDict;
        private void TradeApi_OnRtnOrder(IBaseTdApi arg1, OrderReturnInforDataAny arg2)
        {
            var traded = EnumOrderStatusTypeAny.AllTraded;
            var v_orinal = arg2.VolumeTotalOriginal;
            var status = arg2.OrderStatus;
            var msg = arg2.OrderStatusMsg;
            if (msg == "未成交" || msg == "部分成交")
                NotTradeQueeingDict[arg2.OrderRef] = arg2;
            else if (status == traded || v_orinal == arg2.VolumeTraded || msg == "已撤单")
            {
                if (msg == "全部成交")
                    AllTradedQueeingDict[arg2.OrderRef] = arg2;
                NotTradeQueeingDict.Remove(arg2.OrderRef);
            }
            if (arg2.OrderStatusMsg.Contains("拒绝"))
                Log($"{arg2.Code}:OrderRef为{arg2.OrderRef}==>{arg2.OrderStatusMsg}");
            if(NotTradeQueeingDict.Count()>=1||(NotTradeQueeingDict.Count()==0&& msg == "已撤单")
                ||(NotTradeQueeingDict.Count() == 0 && msg == "全部成交"))
            OnNotTradeQueeing?.Invoke(NotTradeQueeingDict);
        }

        readonly object o = new object();
        /// <summary>
        /// 期指和股票撤单
        /// </summary>
        /// <param name="arg"></param>
        public void GetCancle(OrderReturnInforDataAny arg)
        {
            lock (o)
            {
                try
                {
                    //实盘撤单逻辑
                    if (NotTradeQueeingDict.Keys.Contains(arg.OrderRef))
                        this.TradeApi.OrderAction(arg.Code, arg.OrderSysID, arg.SessionID, arg.FrontID, arg.OrderRef);
                    else
                        this.TradeApi.OrderAction(arg.Code, arg.OrderSysID, arg.SessionID, arg.FrontID, arg.OrderRef);
                    
                }
                catch (Exception e)
                { this.Log("撤单报错:" + e.Message); }
            }
        }
        /// <summary>
        /// 接收委托订单信息
        /// </summary>
        /// <param name="obj"></param>
        private string Form_OnOrderInsertEvent(OrderInsertInfoDataAny obj)
        {
            return TradeApi.OrderInsert(obj.InstrumentID, obj.ExchangeID, obj.Direction, obj.CombOffsetFlag, obj.LimitPrice, obj.VolumeTotalOriginal);
        }

        private bool ReConnectFlag = false;
        private void QuotaApiStock_deep_QuoteApiDisConnected(IBaseMdApi arg1, int arg2)
        {
            //API断线重连

            //启一个线程去执行API重连的任务
            CancellationTokenSource source = new CancellationTokenSource();
            CancellationToken token = source.Token;
            Task d = Task.Run(() =>
            {
                this.Log("行情:" + arg1.ApiType.ToString() + "," + 5 + "秒后自动重连...");
                Thread.Sleep(5000);  //sleepN秒
                ReConnectFlag = true;
                arg1.Init();
            });
            d.Wait(token);
        }

        private void QuotaApiStock_deep_OnRspLogin(IBaseMdApi arg1, OnRspUserLoginAny arg2, int arg3, bool arg4)
        {
            //账号断开后重新连接后订阅合约行情
            if (arg3 == 0)  //登录成功
            {
                ReConnectFlag = false;
                string[] subCodes = allCodeList.ToArray();
                if (subCodes.Length > 0)
                {
                    subCodes.ToList().ForEach((c) => arg1.SubMarketData(c));
                    this.Log("行情:" + arg1.ApiType.ToString() + ",重连订阅代码:" + string.Join(",", subCodes));
                }
                else { this.Log("行情:" + arg1.ApiType.ToString() + ",重连不需要订阅任何代码"); }
            }
        }

        private void QuotaApiStock_deep_OnQuoteData(IBaseMdApi arg1, OnQuoteMarketDataAny arg2)
        {
            行情到达
            var code = arg2.Code;
            var name = arg2.ExchangeInstID;
            var askPrice10 = arg2.AskPrice1;
            var askVol10 = arg2.AskVolume1;

            try
            {
                if (onQuotaData != null && !string.IsNullOrEmpty(arg2.Code))
                {
                    onQuotaData(arg1, arg2);
                }
            }
            catch (Exception e) { this.Log("行情到达_onQuotaData处报错" + e); }
        }


        /// <summary>
        /// 订阅合约
        /// </summary>
        /// <param name="code"></param>
        public void SubStockCode(string code)
        {
            try
            {
                //订阅股票合约
                var it = code.Substring(0, 1);
                if (it == "6")
                {
                    if (!code.Contains(".SSE"))
                        code = code + ".SSE";
                }
                else if (it == "0" || it == "3")
                {
                    if (!code.Contains(".SZSE"))
                        code = code + ".SZSE";
                }
                this.QuotaApiStock_deep.SubMarketData(code);
            }
            catch (Exception ex)
            { Log($"订阅{code}失败,ex:{ex.Message}"); }
        }

        /// <summary>
        /// 获取本地SubCodes.csv文件数据
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private List<string> GetCSVStockCodes(string path)
        {
            ///获取本地csv的股票代码
            var codesList_Bendi = File.ReadAllLines(path, Encoding.Default).ToList();
            var codes = codesList_Bendi.Select(c => c.Split('.')[0]).ToList();
            return codes;
        }

        /// <summary>
        /// 获取本地CodeNameList.csv文件数据
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private List<StockCodeInfo> GetCSVStockNameCodes(string path)
        {
            ///获取本地csv的股票代码
            var codesList_Bendi = File.ReadAllLines(path, Encoding.Default).ToList();
            var codeNames = codesList_Bendi.Select(c => c.Split(',')).ToList();
            var codeNameList = new List<StockCodeInfo>();
            foreach (var item in codeNames)
            {
                codeNameList.Add(
                    new StockCodeInfo
                    {
                        code = item[0],
                        name = item[1],
                        preClosePrice = 0.0
                    });
            }
            return codeNameList;
        }

        /// <summary>
        /// 设置本地CSV文件的值
        /// </summary>
        /// <param name="path"></param>
        public void SetCSVStockCountCodes(int count, string code)
        {
            var codeNameList = new List<StockMixPosition>();
            string path = codeCountListPath;
            try
            {
                var codesList_Bendi = File.ReadAllLines(path, Encoding.Default).ToList();
                var codeNames = codesList_Bendi.Select(c => c.Split(',')).ToList();
                var selectCodeField = codeNames.Where(C => C[0] == code).FirstOrDefault();
                if (selectCodeField.Count() == 0) return;
                if (Convert.ToInt32(selectCodeField[1]) == count)
                    return;
                else
                {
                    selectCodeField[1] = count.ToString();
                    foreach (var item in codeNames)
                    {
                        if (item[0] == code)
                            item[1] = count.ToString();
                        File.WriteAllLines(path, new string[] { }, Encoding.Default);
                    }
                    using (StreamWriter sw = new StreamWriter(path, true))
                    {
                        codeNames.ForEach((c) => sw.WriteLine($"{c[0]},{c[1]}"));

                    }
                    // File.WriteAllLines(path, codeNames, Encoding.Default);
                }
            }
            catch (Exception ex)
            {
                ;
            }
        }

        /// <summary>
        /// 获取本地CodeToCountList.csv文件数据
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private List<StockMixPosition> GetCSVStockCountCodes(string path)
        {
            var codeNameList = new List<StockMixPosition>();
            ///获取本地csv的股票代码
            try
            {
                var codesList_Bendi = File.ReadAllLines(path, Encoding.Default).ToList();
                var codeNames = codesList_Bendi.Select(c => c.Split(',')).ToList();

                foreach (var item in codeNames)
                {
                    try
                    {
                        codeNameList.Add(
                            new StockMixPosition
                            {
                                code = item[0],
                                count = Convert.ToInt32(item[1]),

                            });
                    }
                    catch (Exception ex)
                    { continue; }
                }
                return codeNameList;
            }
            catch (Exception ex)
            {
                return codeNameList;
            }
        }

        /// <summary>
        /// 获取股票的信息
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        public Dictionary<string, StockCodeInfo> GetStockInfor(List<string> codelist)
        {
            return new Dictionary<string, StockCodeInfo>();
            /*
            StockCodeInfo codeinfo_0;
            Dictionary<string, StockCodeInfo> codeinfordict = new Dictionary<string, StockCodeInfo>();
            foreach (var code in codelist)
            {
                if (code == "") { continue; }
                string codeName = "";
                double pre_closePrice = 0.0;
                string id = null;
                var it = code.Substring(0, 1);
                if (it == "6") { id = "SSE"; }
                else { id = "SZSE"; }
                string url = "http://api.shengguanda.com/api/codeinfo/GetCodeinfoByCode?code=" + code + "&exchange=" + id;
                string apikey = "3ad6e2e6-4bba-556e-bd99-b51aa52e5f42";
                var rsp = HttpHelper.Get(url, apikey);
                //Console.WriteLine("respond==" + rsp);
                if (!rsp.IsSuccess) {; }
                else
                {
                    try
                    {
                        CodeInfo dataArray = JsonConvert.DeserializeObject<CodeInfo>(rsp.RspContent);  //如果是多品种则要用DayData[]来接
                        codeName = dataArray.Name;
                    }
                    catch (Exception e) { this.NoteTrader("获取数据中心Name字段出错了:" + e); }
                }
                string yesDate = null;
                string url_price = null;
                for (int i = 1; i < 300; i++)
                {
                    yesDate = DateTime.Now.AddDays(-i).ToString("yyyyMMdd");
                    if (it == "6") { url_price = "http://api.shengguanda.com/api/stockdaily/GetStockDailyByCodeDateFromSSE?date=" + yesDate + "&code=" + code; }
                    else { url_price = "http://api.shengguanda.com/api/stockdaily/GetStockDailyByCodeDateFromSZSE?date=" + yesDate + "&code=" + code; }
                    var rsp_price = HttpHelper.Get(url_price, apikey);
                    if (!rsp_price.IsSuccess) {; }
                    else
                    {
                        try
                        {
                            DayData data_price = JsonConvert.DeserializeObject<DayData>(rsp_price.RspContent);
                            pre_closePrice = (double)(data_price.ClosePrice);
                            break;
                        }
                        catch (Exception e) { continue; }
                    }
                }
                codeinfo_0.code = code;
                codeinfo_0.name = codeName;
                codeinfo_0.preClosePrice = pre_closePrice;
                codeinfordict[code] = codeinfo_0;
            }
            return codeinfordict;
            */
        }

        public override void End()
        {

        }
    }
}

 

博主非常乐意看到大家可以一起为这套系统进行贡献,希望量化(程序化)交易可以降低门槛,惠及大众。。。

 

qq:1013359736

 

 

 

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值