先扯淡几句。。。
一个出身非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