diff --git a/.gitignore b/.gitignore
index 96374c4..dfcfd56 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,43 +1,350 @@
-# Windows image file caches
-Thumbs.db
-ehthumbs.db
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
-# Folder config file
-Desktop.ini
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
-# Recycle Bin used on file shares
-$RECYCLE.BIN/
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
-# Windows Installer files
-*.cab
-*.msi
-*.msm
-*.msp
+# Mono auto generated files
+mono_crash.*
-# Windows shortcuts
-*.lnk
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
-# =========================
-# Operating System Files
-# =========================
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
-# OSX
-# =========================
+# Visual Studio 2017 auto generated files
+Generated\ Files/
-.DS_Store
-.AppleDouble
-.LSOverride
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
-# Thumbnails
-._*
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
-# Files that might appear on external disk
-.Spotlight-V100
-.Trashes
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
-# Directories potentially created on remote AFP share
-.AppleDB
-.AppleDesktop
-Network Trash Folder
-Temporary Items
-.apdisk
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
diff --git a/QuantBox.API.Provider/QuantBox.APIProvider.csproj b/QuantBox.API.Provider/QuantBox.APIProvider.csproj
deleted file mode 100644
index 9ca24b4..0000000
--- a/QuantBox.API.Provider/QuantBox.APIProvider.csproj
+++ /dev/null
@@ -1,147 +0,0 @@
-
-
-
-
- Debug
- AnyCPU
- {E86E1057-B344-4805-A836-43606154677E}
- Library
- Properties
- QuantBox.APIProvider
- QuantBox.APIProvider
- v4.6.1
- 512
-
-
-
- true
- full
- false
- C:\Program Files\SmartQuant Ltd\OpenQuant 2014\
- DEBUG;TRACE
- prompt
- 4
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
-
- ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll
-
-
- ..\packages\NLog.4.5.6\lib\net45\NLog.dll
-
-
- False
- C:\Program Files\SmartQuant Ltd\OpenQuant 2014\SmartQuant.dll
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- False
- C:\Program Files\SmartQuant Ltd\OpenQuant 2014\XAPI_CSharp.exe
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Form
-
-
- ApiControlForm.cs
-
-
- Form
-
-
- ApiManagerForm.cs
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ApiControlForm.cs
-
-
- ApiManagerForm.cs
-
-
-
-
- {d8d82e27-47f5-4579-92ad-416da86ad601}
- QuantBox.Extensions
-
-
-
-
-
\ No newline at end of file
diff --git a/QuantBox.API.Provider/QuantBox.APIProvider.csproj.user b/QuantBox.API.Provider/QuantBox.APIProvider.csproj.user
deleted file mode 100644
index ee82c1f..0000000
--- a/QuantBox.API.Provider/QuantBox.APIProvider.csproj.user
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
- Program
- C:\Program Files\SmartQuant Ltd\OpenQuant 2014\OpenQuant.x86.exe
-
-
\ No newline at end of file
diff --git a/QuantBox.API.Provider/QuantBox.APIProvider_Linux.csproj b/QuantBox.API.Provider/QuantBox.APIProvider_Linux.csproj
deleted file mode 100644
index 58a1aa7..0000000
--- a/QuantBox.API.Provider/QuantBox.APIProvider_Linux.csproj
+++ /dev/null
@@ -1,95 +0,0 @@
-
-
-
-
- Debug
- AnyCPU
- {E86E1057-B344-4805-A836-43606154677E}
- Library
- Properties
- QuantBox.APIProvider
- QuantBox.APIProvider
- v4.5.1
- 512
-
-
-
- true
- full
- false
- C:\Program Files\SmartQuant Ltd\OpenQuant 2014\
- DEBUG;TRACE
- prompt
- 4
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..\packages\Newtonsoft.Json.6.0.5\lib\net45\Newtonsoft.Json.dll
-
-
- ..\packages\NLog.3.1.0.0\lib\net45\NLog.dll
-
-
- ..\..\bin\SmartQuant.dll
-
-
- ..\..\bin\QuantBox.XAPI.dll
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {D8D82E27-47F5-4579-92AD-416DA86AD601}
- QuantBox.Extensions_Linux
-
-
-
\ No newline at end of file
diff --git a/QuantBox.API.Provider/Single/ServerItem.cs b/QuantBox.API.Provider/Single/ServerItem.cs
deleted file mode 100644
index 46dd4c2..0000000
--- a/QuantBox.API.Provider/Single/ServerItem.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-using XAPI;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace QuantBox.APIProvider.Single
-
-{
- [DefaultProperty("Label")]
- public class ServerItem : ICloneable
- {
- private const string OPEN_QUANT = "OpenQuant";
- [Category("标签")]
- public string Label
- {
- get;
- set;
- }
- ///
- /// 订阅主题
- ///
- [Category("行情 - Femas")]
- [Description("Femas")]
- public int TopicId { get; set; }
-
- ///
- /// 流恢复
- ///
- [Category("流重传方式")]
- public ResumeType MarketDataTopicResumeType { get; set; }
- [Category("流重传方式")]
- public ResumeType PrivateTopicResumeType { get; set; }
- [Category("流重传方式")]
- public ResumeType PublicTopicResumeType { get; set; }
- [Category("流重传方式")]
- public ResumeType UserTopicResumeType { get; set; }
- ///
- /// 经纪公司代码
- ///
- [Category("服务端信息")]
- public string BrokerID { get; set; }
- ///
- /// 用户端产品信息
- ///
- [Category("客户端认证 - CTP/LTS")]
- public string UserProductInfo { get; set; }
- ///
- /// 认证码
- ///
- [Category("客户端认证 - CTP/LTS")]
- public string AuthCode { get; set; }
- ///
- /// 地址
- ///
- [Category("服务端信息")]
- public string Address { get; set; }
- ///
- /// 地址
- ///
- [Category("扩展信息")]
- public string ExtInfoChar128 { get; set; }
- [Category("扩展信息")]
- public string ConfigPath { get; set; }
- ///
- /// 端口号
- ///
- [Category("服务端信息")]
- public int Port { get; set; }
- ///
- /// UDP行情
- ///
- [Category("行情")]
- [Description("CTP/DFITC_Level2")]
- public bool IsUsingUdp { get; set; }
- ///
- /// 多播行情
- ///
- [Category("行情")]
- [Description("CTP")]
- public bool IsMulticast { get; set; }
-
- public ServerItem()
- {
- UserProductInfo = OPEN_QUANT;
- }
-
- public ServerInfoField ToStruct()
- {
- ServerInfoField field = new ServerInfoField();
- field.IsUsingUdp = this.IsUsingUdp;
- field.IsMulticast = this.IsMulticast;
- field.TopicId = this.TopicId;
- field.Port = this.Port;
- field.MarketDataTopicResumeType = this.MarketDataTopicResumeType;
- field.PrivateTopicResumeType = this.PrivateTopicResumeType;
- field.PublicTopicResumeType = this.PublicTopicResumeType;
- field.UserTopicResumeType = this.UserTopicResumeType;
- field.BrokerID = this.BrokerID;
- field.UserProductInfo = this.UserProductInfo;
- field.AuthCode = this.AuthCode;
- field.Address = this.Address;
- field.ConfigPath = this.ConfigPath;
- field.ExtInfoChar128 = this.ExtInfoChar128;
-
- return field;
- }
-
- public override string ToString()
- {
- return string.Format("Label={0};BrokerID={1};Address={2}", this.Label, this.BrokerID, this.Address);
- }
-
- public object Clone()
- {
- return this.MemberwiseClone();
- }
- }
-}
diff --git a/QuantBox.API.Provider/Single/SessionTimeItem.cs b/QuantBox.API.Provider/Single/SessionTimeItem.cs
deleted file mode 100644
index 069f94c..0000000
--- a/QuantBox.API.Provider/Single/SessionTimeItem.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using Newtonsoft.Json;
-using OrderedPropertyGrid;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace QuantBox.APIProvider.Single
-{
- // 发现想让属性显示排序,但发现Json转换出问题了,所以还是决定不排序了
- [TypeConverter(typeof(PropertySorter))]
- [JsonConverter(typeof(NoTypeConverterJsonConverter))]
- public class SessionTimeItem
- {
- [PropertyOrder(1)]
- public TimeSpan SessionStart { get; set; }
- [PropertyOrder(2)]
- public TimeSpan SessionEnd { get; set; }
- [PropertyOrder(3)]
- public bool Enable { get; set; }
-
- public override string ToString()
- {
- //return string.Format("Start={0};End={1}", this.SessionStart, this.SessionEnd);
- if (Enable)
- {
- return string.Format("+|Start={0};End={1}", this.SessionStart, this.SessionEnd);
- }
- else
- {
-
- return string.Format("-|Start={0};End={1}", this.SessionStart, this.SessionEnd);
- }
- }
- }
-}
diff --git a/QuantBox.API.Provider/Single/SingleProvider.API.cs b/QuantBox.API.Provider/Single/SingleProvider.API.cs
deleted file mode 100644
index 7547be8..0000000
--- a/QuantBox.API.Provider/Single/SingleProvider.API.cs
+++ /dev/null
@@ -1,264 +0,0 @@
-using SmartQuant;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-using XAPI.Callback;
-using XAPI;
-using NLog;
-using System.Reflection;
-using QuantBox.Extensions;
-using System.IO;
-
-namespace QuantBox.APIProvider.Single
-{
- public partial class SingleProvider
- {
- static SingleProvider()
- {
- NLog.LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(Path.Combine(PathHelper.RootPath.LocalPath, "NLog.config"), true);
- }
- public DelegateOnRspQryInvestorPosition OnRspQryInvestorPosition { get; set; }
- public DelegateOnRspQryTradingAccount OnRspQryTradingAccount { get; set; }
- public DelegateOnRtnInstrumentStatus OnRtnInstrumentStatus { get; set; }
- public DelegateOnRtnOrder OnRtnOrder { get; set; }
- public DelegateOnRtnTrade OnRtnTrade { get; set; }
-
- public DelegateOnRspQryOrder OnRspQryOrder { get; set; }
- public DelegateOnRspQryTrade OnRspQryTrade { get; set; }
-
- //记录合约列表,从实盘合约名到对象的映射
- private readonly Dictionary _dictInstruments = new Dictionary();
- private readonly Dictionary _dictInstrumentsStatus = new Dictionary();
-
- public static int GetDate(DateTime dt)
- {
- return dt.Year * 10000 + dt.Month * 100 + dt.Day;
- }
-
- public static int GetTime(DateTime dt)
- {
- return dt.Hour * 10000 + dt.Minute * 100 + dt.Second;
- }
-
- public static DateTime GetDateTime(int yyyyMMdd, int hhmmss, int Millisecond)
- {
- int yyyy = yyyyMMdd / 10000;
- int MM = yyyyMMdd % 10000 / 100;
- int dd = yyyyMMdd % 100;
- int hh = hhmmss / 10000;
- int mm = hhmmss % 10000 / 100;
- int ss = hhmmss % 100;
- DateTime dt = new DateTime(yyyy, MM, dd, hh, mm, ss, Millisecond);
- return dt;
- }
-
- private void OnRspQryInstrument_callback(object sender, ref InstrumentField instrument, int size1, bool bIsLast)
- {
- if (size1 <= 0)
- {
- (sender as XApi).GetLog().Info("OnRspQryInstrument");
- return;
- }
-
- _dictInstruments[instrument.Symbol] = instrument;
-
- if (bIsLast)
- {
- (sender as XApi).GetLog().Info("合约列表已经接收完成,共 {0} 条", _dictInstruments.Count);
- }
- }
-
- private void OnRspQryTradingAccount_callback(object sender, ref AccountField account, int size1, bool bIsLast)
- {
- if (size1 <= 0)
- {
- (sender as XApi).GetLog().Info("OnRspQryTradingAccount");
- }
- else
- {
- if (IsLogOnRspQryTradingAccount)
- (sender as XApi).GetLog().Info("OnRspQryTradingAccount:" + account.ToFormattedString());
- }
-
- // 由策略来收回报
- if (OnRspQryTradingAccount != null)
- OnRspQryTradingAccount(sender, ref account, size1, bIsLast);
-
- if (size1 <= 0)
- return;
-
- if (!IsConnected)
- return;
-
- string currency = "CNY";
-
- AccountData ad = new AccountData(DateTime.Now, AccountDataType.AccountValue,
- account.AccountID, this.id, this.id);
-
- Type type = typeof(AccountField);
- FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
- foreach (FieldInfo field in fields)
- {
- ad.Fields.Add(field.Name, currency, field.GetValue(account));
- }
- // 将对像完全设置进去,等着取出
- ad.Fields.Add(AccountDataFieldEx.USER_DATA, currency, account);
- ad.Fields.Add(AccountDataFieldEx.DATE, currency, GetDate(DateTime.Today));
-
-
- try
- {
- EmitAccountData(ad);
- }
- catch (Exception ex)
- {
- (sender as XApi).GetLog().Error(ex);
- }
- }
-
- private void OnRspQryInvestor_callback(object sender, ref InvestorField investor, int size1, bool bIsLast)
- {
- if (size1 <= 0)
- {
- (sender as XApi).GetLog().Info("OnRspQryInvestor");
- return;
- }
-
- (sender as XApi).GetLog().Info("OnRspQryInvestor:{0}", investor.ToFormattedString());
- }
-
- private void OnRspQryInvestorPosition_callback(object sender, ref PositionField position, int size1, bool bIsLast)
- {
- if (size1 <= 0)
- {
- (sender as XApi).GetLog().Info("OnRspQryInvestorPosition");
- }
- else if (position.Position != 0)
- {
- // UFX中已经过期的持仓也会推送,所以这里过滤一下不显示
- if (IsLogOnRspQryInvestorPosition)
- (sender as XApi).GetLog().Info("OnRspQryInvestorPosition:" + position.ToFormattedString());
- }
-
- // 由策略来收回报
- if (OnRspQryInvestorPosition != null)
- OnRspQryInvestorPosition(sender, ref position, size1, bIsLast);
-
- if (size1 <= 0)
- return;
-
- if (!IsConnected)
- return;
-
- PositionFieldEx item;
- if (!positions.TryGetValue(position.Symbol, out item))
- {
- item = new PositionFieldEx();
- positions[position.Symbol] = item;
- }
- item.AddPosition(position);
-
- AccountData ad = new AccountData(DateTime.Now, AccountDataType.Position,
- position.AccountID, this.id, this.id);
-
- ad.Fields.Add(AccountDataField.SYMBOL, item.Symbol);
- ad.Fields.Add(AccountDataField.EXCHANGE, item.Exchange);
- ad.Fields.Add(AccountDataField.QTY, item.Qty);
- ad.Fields.Add(AccountDataField.LONG_QTY, item.LongQty);
- ad.Fields.Add(AccountDataField.SHORT_QTY, item.ShortQty);
-
- ad.Fields.Add(AccountDataFieldEx.USER_DATA, item);
- ad.Fields.Add(AccountDataFieldEx.DATE, GetDate(DateTime.Today));
-
- try
- {
- EmitAccountData(ad);
- }
- catch (Exception ex)
- {
- (sender as XApi).GetLog().Error(ex);
- }
- }
-
- private void OnRspQrySettlementInfo_callback(object sender, ref SettlementInfoClass settlementInfo, int size1, bool bIsLast)
- {
- if (size1 <= 0)
- {
- (sender as XApi).GetLog().Info("OnRspQrySettlementInfo");
- return;
- }
-
- if (bIsLast)
- {
- (sender as XApi).GetLog().Info("OnRspQrySettlementInfo:" + Environment.NewLine + settlementInfo.Content);
- }
- }
-
-
- private void OnRspQryQuote_callback(object sender, ref QuoteField quote, int size1, bool bIsLast)
- {
- if (size1 <= 0)
- {
- (sender as XApi).GetLog().Info("OnRspQryQuote");
- return;
- }
-
- (sender as XApi).GetLog().Info("OnRspQryQuote:" + quote.ToFormattedString());
- }
-
- private void OnRspQryTrade_callback(object sender, ref TradeField trade, int size1, bool bIsLast)
- {
- if (size1 <= 0)
- {
- (sender as XApi).GetLog().Info("OnRspQryTrade");
-
- }
- else
- {
- (sender as XApi).GetLog().Info("OnRspQryTrade:" + trade.ToFormattedString());
- }
- if (OnRspQryTrade != null)
- OnRspQryTrade(this, ref trade, size1, bIsLast);
- }
-
- private void OnRspQryOrder_callback(object sender, ref OrderField order, int size1, bool bIsLast)
- {
- if (size1 <= 0)
- {
- (sender as XApi).GetLog().Info("OnRspQryOrder");
- }
- else
- {
- (sender as XApi).GetLog().Info("OnRspQryOrder:" + order.ToFormattedString());
- }
- if (OnRspQryOrder != null)
- OnRspQryOrder(this, ref order, size1, bIsLast);
- }
-
- private void OnRtnInstrumentStatus_callback(object sender, ref InstrumentStatusField instrumentStatus)
- {
- if (OnRtnInstrumentStatus != null)
- OnRtnInstrumentStatus(sender, ref instrumentStatus);
-
- // 记录下来,后期可能要用到
- _dictInstrumentsStatus[instrumentStatus.Symbol] = instrumentStatus;
-
- // 合约状态信息太多了,也不关心,这里屏蔽显示
- if (IsLogOnRtnInstrumentStatus)
- (sender as XApi).GetLog().Info("OnRtnInstrumentStatus:" + instrumentStatus.ToFormattedString());
- }
-
- public InstrumentStatusField GetInstrumentStatus(string symbol)
- {
- InstrumentStatusField instrumentStatus;
- if (_dictInstrumentsStatus.TryGetValue(symbol, out instrumentStatus))
- {
- return instrumentStatus;
- }
- return instrumentStatus;
- }
- }
-}
diff --git a/QuantBox.API.Provider/Single/UserItem.cs b/QuantBox.API.Provider/Single/UserItem.cs
deleted file mode 100644
index 5a40954..0000000
--- a/QuantBox.API.Provider/Single/UserItem.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using XAPI;
-
-namespace QuantBox.APIProvider.Single
-{
- [DefaultProperty("Label")]
- public class UserItem:ICloneable
- {
- [Category("标签")]
- public string Label
- {
- get;
- set;
- }
- ///
- /// 用户代码
- ///
- [Category("账号")]
- public string UserID { get; set; }
- ///
- /// 密码
- ///
- [Category("账号")]
- public string Password { get; set; }
- ///
- /// 扩展信息
- ///
- [Category("账号")]
- public string ExtInfoChar64 { get; set; }
- [Category("账号")]
- public int ExtInfoInt32 { get; set; }
-
- public UserInfoField ToStruct()
- {
- UserInfoField field = new UserInfoField();
-
- field.UserID = this.UserID;
- field.Password = this.Password;
- field.ExtInfoChar64 = this.ExtInfoChar64;
- field.ExtInfoInt32 = this.ExtInfoInt32;
-
- return field;
- }
-
- public override string ToString()
- {
- return string.Format("Label={0};UserID={1}", this.Label,this.UserID);
- }
-
- public object Clone()
- {
- return this.MemberwiseClone();
- }
- }
-}
diff --git a/QuantBox.API.Provider/app.config b/QuantBox.API.Provider/app.config
deleted file mode 100644
index 99ddf3e..0000000
--- a/QuantBox.API.Provider/app.config
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/QuantBox.API.Provider/packages.config b/QuantBox.API.Provider/packages.config
deleted file mode 100644
index 5656d2a..0000000
--- a/QuantBox.API.Provider/packages.config
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/QuantBox.APIProvider.sln b/QuantBox.APIProvider.sln
new file mode 100644
index 0000000..ecede0b
--- /dev/null
+++ b/QuantBox.APIProvider.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29411.108
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuantBox.Extensions", "QuantBox.Extensions\QuantBox.Extensions.csproj", "{1A772F95-B670-40AC-8D29-2B82A4B95E12}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuantBox.APIProvider", "QuantBox.APIProvider\QuantBox.APIProvider.csproj", "{DD59FAE6-8CA6-4389-B934-06F26A08D289}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1A772F95-B670-40AC-8D29-2B82A4B95E12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1A772F95-B670-40AC-8D29-2B82A4B95E12}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1A772F95-B670-40AC-8D29-2B82A4B95E12}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1A772F95-B670-40AC-8D29-2B82A4B95E12}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DD59FAE6-8CA6-4389-B934-06F26A08D289}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DD59FAE6-8CA6-4389-B934-06F26A08D289}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DD59FAE6-8CA6-4389-B934-06F26A08D289}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DD59FAE6-8CA6-4389-B934-06F26A08D289}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {DE721877-AECD-4501-976D-2AA3C52F9CE0}
+ EndGlobalSection
+ GlobalSection(MonoDevelopProperties) = preSolution
+ StartupItem = SMACrossover\Realtime\Realtime.csproj
+ EndGlobalSection
+EndGlobal
diff --git a/QuantBox.APIProvider/CmdLine.cs b/QuantBox.APIProvider/CmdLine.cs
new file mode 100644
index 0000000..f1aee41
--- /dev/null
+++ b/QuantBox.APIProvider/CmdLine.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+using CommandLine;
+
+#if NET48
+using System.Windows.Forms;
+#endif
+
+namespace QuantBox.APIProvider
+{
+ public class Options
+ {
+ [Option('f', "file", Required = false, HelpText = "策略项目文件")]
+ public string file { get; set; }
+
+ [Option('i', "id", Required = false, HelpText = "实例数字ID,用于收到剪贴板事件时过滤使用")]
+ public int id { get; set; }
+
+ [Option('r', "run", Required = false, Default = false, HelpText = "运行策略")]
+ public bool run { get; set; }
+
+ [Option('s', "stop", Required = false, Default = false, HelpText = "停止策略")]
+ public bool stop { get; set; }
+
+ [Option('e', "exit", Required = false, Default = false, HelpText = "退出程序")]
+ public bool exit { get; set; }
+ }
+
+ public class CmdLine
+ {
+ private int id;
+
+ public void ParseForStart(ProviderHost host)
+ {
+ // 通过命令行启动策略
+ //cd "C:\Program Files\SmartQuant Ltd\OpenQuant 2014"
+ //C:
+ //start OpenQuant.exe --file="D:\Users\Kan\Documents\OpenQuant 2014\Solutions\SMACrossover\SMACrossover.sln" --id=100 --run
+
+ var args = Environment.GetCommandLineArgs();
+ var text = Environment.CommandLine;
+ Console.WriteLine($"命令行: {text}");
+ CommandLine.Parser.Default.ParseArguments(args)
+ .WithParsed(opts => RunOptions(opts, host))
+ .WithNotParsed((errs) => HandleParseError(errs));
+ }
+
+ public void ParseForStop(ProviderHost host)
+ {
+#if NET48
+ //echo --id=100 --stop --exit | clip
+ IDataObject ido = Clipboard.GetDataObject();
+
+ if (!ido.GetDataPresent(DataFormats.Text))
+ return;
+
+ var text = ido.GetData(DataFormats.Text) as string;
+ Console.WriteLine($"剪贴板: {text}");
+ CommandLine.Parser.Default.ParseArguments(text.Split(' '))
+ .WithParsed(opts => ExitOptions(opts, host))
+ .WithNotParsed((errs) => HandleParseError(errs));
+#endif
+ }
+
+ void RunOptions(Options opts, ProviderHost host)
+ {
+ // 记下ID,退出时使用
+ id = opts.id;
+
+ if (string.IsNullOrEmpty(opts.file))
+ return;
+
+ if (!(new FileInfo(opts.file).Exists))
+ return;
+
+ host.Solution_Start_Thread(opts);
+ }
+
+ void ExitOptions(Options opts, ProviderHost host)
+ {
+ if (id != opts.id)
+ return;
+
+ host.Solution_Stop_Thread(opts);
+ }
+
+ void HandleParseError(IEnumerable errs)
+ {
+ // Application.Exit();
+ }
+ }
+}
diff --git a/QuantBox.API.Provider/ControlExtention.cs b/QuantBox.APIProvider/ControlExtention.cs
similarity index 91%
rename from QuantBox.API.Provider/ControlExtention.cs
rename to QuantBox.APIProvider/ControlExtention.cs
index abf4943..696e84e 100644
--- a/QuantBox.API.Provider/ControlExtention.cs
+++ b/QuantBox.APIProvider/ControlExtention.cs
@@ -3,10 +3,13 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using System.Windows.Forms;
+
namespace QuantBox.APIProvider
{
+#if NET48
+ using System.Windows.Forms;
+
public static class ControlExtention
{
public delegate void InvokeHandler();
@@ -23,4 +26,5 @@ public static void SafeInvoke(this Control control, InvokeHandler handler)
}
}
}
+#endif
}
diff --git a/QuantBox.API.Provider/Host/ProviderHost.cs b/QuantBox.APIProvider/Host/ProviderHost.cs
similarity index 76%
rename from QuantBox.API.Provider/Host/ProviderHost.cs
rename to QuantBox.APIProvider/Host/ProviderHost.cs
index c29d2cc..836ab8d 100644
--- a/QuantBox.API.Provider/Host/ProviderHost.cs
+++ b/QuantBox.APIProvider/Host/ProviderHost.cs
@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+
using SmartQuant;
using System.IO;
@@ -10,7 +8,10 @@
using System.ComponentModel;
using QuantBox.APIProvider.Single;
using System.Reflection;
-using System.Windows.Forms;
+using CommandLine;
+using ClipboardMonitor;
+using System.Threading;
+
namespace QuantBox.APIProvider
{
@@ -18,7 +19,7 @@ namespace QuantBox.APIProvider
/// Provder宿主
/// 由它进行其它Provder的初始创建,以及订单的路由
///
- public class ProviderHost : Provider, IExecutionProvider, IDataProvider
+ public partial class ProviderHost : Provider, IExecutionProvider, IDataProvider
{
#region Provider
[Description("创建Provder的记录文件路径")]
@@ -48,8 +49,6 @@ public string Version
get { return Assembly.GetExecutingAssembly().GetName().Version.ToString(); }
}
-
-
public ProviderHost(Framework framework)
: base(framework)
{
@@ -67,6 +66,22 @@ public ProviderHost(Framework framework)
Create(ProviderList);
ProviderList.ListChanged += ProviderList_ListChanged;
+
+#if NET48
+ cmdLine = new CmdLine();
+ try
+ {
+ cmdLine.ParseForStart(this);
+ }
+ catch
+ {
+
+ }
+
+
+ new ClipboardNotifications();
+ ClipboardNotifications.ClipboardUpdate += ClipboardNotifications_ClipboardUpdate;
+#endif
}
~ProviderHost()
@@ -79,53 +94,19 @@ void ProviderList_ListChanged(object sender, ListChangedEventArgs e)
Save(ConfigPath);
}
- //private static bool _bMenuAdded;
-
public void Init(byte id, string name)
{
base.id = id;
base.name = name;
base.description = "QuantBox API Provider Host";
base.url = "www.quantbox.cn";
-
- //if (!_bMenuAdded)
- //{
- // try
- // {
- // // DOS窗口时没有问题,非DOS下异常,所以可以利用一下
- // Console.Clear();
- // }
- // catch
- // {
- // Console.WriteLine("要创建自己的菜单");
- // // 只有DOS窗口时要注意
- // System.Threading.ThreadPool.QueueUserWorkItem(delegate
- // {
- // while (Application.OpenForms.Count == 0 || Application.OpenForms[0].Name != "MainForm")
- // {
- // System.Threading.Thread.Sleep(1000);
- // }
- // Form mainForm = Application.OpenForms[0];
-
- // try
- // {
- // mainForm.SafeInvoke(() => { AddToolStripMenuItem(mainForm); });
- // }
- // catch (Exception e)
- // {
- // // 奇怪,调试的时候总是会出错
- // Console.WriteLine(e);
- // }
- // });
- // }
- // _bMenuAdded = true;
- //}
}
///
/// 指定保存格式
///
- private static readonly JsonSerializerSettings JSetting = new JsonSerializerSettings() {
+ private static readonly JsonSerializerSettings JSetting = new JsonSerializerSettings()
+ {
//NullValueHandling = NullValueHandling.Ignore,
//DefaultValueHandling = DefaultValueHandling.Ignore,
Formatting = Formatting.Indented,
@@ -134,17 +115,19 @@ public void Init(byte id, string name)
// 读取信息
public void Load(string path)
{
- try {
+ try
+ {
object ret;
- using (TextReader reader = new StreamReader(path)) {
+ using (TextReader reader = new StreamReader(path))
+ {
ret = JsonConvert.DeserializeObject(reader.ReadToEnd(), ProviderList.GetType());
reader.Close();
}
ProviderList = ret as BindingList;
}
- catch
+ catch(Exception ex)
{
- // ignored
+ Console.WriteLine(ex);
}
}
@@ -153,7 +136,8 @@ public void Save(string path)
if (ProviderList == null)
return;
- using (TextWriter writer = new StreamWriter(path)) {
+ using (TextWriter writer = new StreamWriter(path))
+ {
writer.Write("{0}", JsonConvert.SerializeObject(ProviderList, ProviderList.GetType(), JSetting));
writer.Close();
}
@@ -174,9 +158,11 @@ public override void Disconnect()
public void Create(IList list)
{
- foreach (var l in list) {
+ foreach (var l in list)
+ {
IProvider pvd = framework.ProviderManager.GetProvider(l.Id);
- if (pvd == null) {
+ if (pvd == null)
+ {
SingleProvider provider = new SingleProvider(framework);
provider.Init(l.Id, l.Name);
framework.ProviderManager.AddProvider(provider);
@@ -202,7 +188,7 @@ public IExecutionProvider ExecutionProvider
}
public override void Send(ExecutionCommand command)
{
- if(command.RouteId == id)
+ if (command.RouteId == id)
return;
IExecutionProvider provider;
diff --git a/QuantBox.APIProvider/Host/ProviderHost_UI.cs b/QuantBox.APIProvider/Host/ProviderHost_UI.cs
new file mode 100644
index 0000000..b715a89
--- /dev/null
+++ b/QuantBox.APIProvider/Host/ProviderHost_UI.cs
@@ -0,0 +1,161 @@
+using System;
+
+using SmartQuant;
+using System.IO;
+using System.Reflection;
+
+using CommandLine;
+
+using System.Threading;
+
+#if NET48
+using ClipboardMonitor;
+using System.Windows.Forms;
+#endif
+
+namespace QuantBox.APIProvider
+{
+ ///
+ /// Provder宿主
+ /// 由它进行其它Provder的初始创建,以及订单的路由
+ ///
+ public partial class ProviderHost
+ {
+ private CmdLine cmdLine = null;
+
+ private void ClipboardNotifications_ClipboardUpdate(object sender, EventArgs e)
+ {
+ try
+ {
+ cmdLine.ParseForStop(this);
+ }
+ catch
+ {
+
+ }
+ }
+
+ private object GetSolutionManager()
+ {
+ // OpenQuant.Global.SolutionManager是静态属性,可以通过Get方式获得
+ var g = Assembly.GetEntryAssembly().GetType("OpenQuant.Global");
+ var sm = g.GetProperty("SolutionManager");
+ return sm.GetGetMethod().Invoke(null, null);
+ }
+
+ private void LoadSolution(object solutionManager, string filename)
+ {
+ var type = solutionManager.GetType();
+ var m = type.GetMethod("LoadSolution", BindingFlags.NonPublic | BindingFlags.Instance);
+ m.Invoke(solutionManager, new object[] { new FileInfo(filename) });
+ }
+
+ public void Solution_Start_Thread(Options opts)
+ {
+#if NET48
+ System.Threading.ThreadPool.QueueUserWorkItem(delegate
+ {
+ DateTime dt = DateTime.Now;
+ // 检查界面是否正常启动
+ var mainForm = GetMainForm();
+ while (mainForm == null)
+ {
+ Thread.Sleep(1000);
+ mainForm = GetMainForm();
+
+ // 如果1分钟找不到就退出循环
+ var ts = DateTime.Now - dt;
+ if (ts.TotalSeconds > 60)
+ {
+ return;
+ }
+ }
+
+ var sm = GetSolutionManager();
+ Thread.Sleep(1000);
+ mainForm.SafeInvoke(() =>
+ {
+ LoadSolution(sm, opts.file);
+ });
+ if (opts.run)
+ {
+ Thread.Sleep(3000);
+ mainForm.SafeInvoke(() =>
+ {
+ Solution_Start(mainForm);
+ });
+ }
+ });
+#endif
+ }
+
+ public void Solution_Stop_Thread(Options opts)
+ {
+#if NET48
+ System.Threading.ThreadPool.QueueUserWorkItem(delegate
+ {
+ var mainForm = GetMainForm();
+ if (mainForm == null)
+ {
+ return;
+ }
+ var sm = GetSolutionManager();
+ if (opts.stop)
+ {
+ // 没有停止的需要停止才能退出
+ if (framework.StrategyManager.Status != StrategyStatus.Stopped)
+ {
+ Thread.Sleep(1000);
+ mainForm.SafeInvoke(() =>
+ {
+ Solution_Stop(mainForm);
+ });
+ }
+ }
+ if (opts.exit)
+ {
+ Thread.Sleep(3000);
+ mainForm.SafeInvoke(() =>
+ {
+ File_Exit(mainForm);
+ });
+ }
+ });
+#endif
+ }
+
+
+#if NET48
+
+ private Form GetMainForm()
+ {
+ foreach (Form f in Application.OpenForms)
+ {
+ if (f.Name == "MainForm")
+ return f;
+ }
+ return null;
+ }
+ private void Solution_Start(Form from)
+ {
+ Type type = from.GetType();
+ var m = type.GetMethod("menuSolution_Start_Click", BindingFlags.NonPublic | BindingFlags.Instance);
+ m.Invoke(from, new object[] { null, null });
+ }
+
+ private void Solution_Stop(Form from)
+ {
+ Type type = from.GetType();
+ var m = type.GetMethod("menuSolution_Stop_Click", BindingFlags.NonPublic | BindingFlags.Instance);
+ m.Invoke(from, new object[] { null, null });
+ }
+
+ private void File_Exit(Form from)
+ {
+ Type type = from.GetType();
+ var m = type.GetMethod("menuFile_Exit_Click", BindingFlags.NonPublic | BindingFlags.Instance);
+ m.Invoke(from, new object[] { null, null });
+ }
+#endif
+ }
+}
diff --git a/QuantBox.API.Provider/Host/ProviderItem.cs b/QuantBox.APIProvider/Host/ProviderItem.cs
similarity index 100%
rename from QuantBox.API.Provider/Host/ProviderItem.cs
rename to QuantBox.APIProvider/Host/ProviderItem.cs
diff --git a/QuantBox.API.Provider/PathHelper.cs b/QuantBox.APIProvider/PathHelper.cs
similarity index 100%
rename from QuantBox.API.Provider/PathHelper.cs
rename to QuantBox.APIProvider/PathHelper.cs
diff --git a/QuantBox.API.Provider/Properties/AssemblyInfo.cs b/QuantBox.APIProvider/Properties/AssemblyInfo.cs
similarity index 100%
rename from QuantBox.API.Provider/Properties/AssemblyInfo.cs
rename to QuantBox.APIProvider/Properties/AssemblyInfo.cs
diff --git a/QuantBox.API.Provider/Properties/DataSources/QuantBox.APIProvider.Single.ApiItem.datasource b/QuantBox.APIProvider/Properties/DataSources/QuantBox.APIProvider.Single.ApiItem.datasource
similarity index 100%
rename from QuantBox.API.Provider/Properties/DataSources/QuantBox.APIProvider.Single.ApiItem.datasource
rename to QuantBox.APIProvider/Properties/DataSources/QuantBox.APIProvider.Single.ApiItem.datasource
diff --git a/QuantBox.API.Provider/Properties/DataSources/QuantBox.APIProvider.Single.ServerItem.datasource b/QuantBox.APIProvider/Properties/DataSources/QuantBox.APIProvider.Single.ServerItem.datasource
similarity index 100%
rename from QuantBox.API.Provider/Properties/DataSources/QuantBox.APIProvider.Single.ServerItem.datasource
rename to QuantBox.APIProvider/Properties/DataSources/QuantBox.APIProvider.Single.ServerItem.datasource
diff --git a/QuantBox.API.Provider/Properties/DataSources/QuantBox.APIProvider.Single.UserItem.datasource b/QuantBox.APIProvider/Properties/DataSources/QuantBox.APIProvider.Single.UserItem.datasource
similarity index 100%
rename from QuantBox.API.Provider/Properties/DataSources/QuantBox.APIProvider.Single.UserItem.datasource
rename to QuantBox.APIProvider/Properties/DataSources/QuantBox.APIProvider.Single.UserItem.datasource
diff --git a/QuantBox.API.Provider/PropertySorter.cs b/QuantBox.APIProvider/PropertySorter.cs
similarity index 100%
rename from QuantBox.API.Provider/PropertySorter.cs
rename to QuantBox.APIProvider/PropertySorter.cs
diff --git a/QuantBox.APIProvider/QuantBox.APIProvider.csproj b/QuantBox.APIProvider/QuantBox.APIProvider.csproj
new file mode 100644
index 0000000..bfa0720
--- /dev/null
+++ b/QuantBox.APIProvider/QuantBox.APIProvider.csproj
@@ -0,0 +1,36 @@
+
+
+
+ netstandard2.0;net48
+ false
+ QuantBox.APIProvider
+ QuantBox.APIProvider
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ C:\Program Files\SmartQuant Ltd\OpenQuant 2014\SmartQuant.dll
+
+
+
+
+ C:\Program Files\SmartQuant Ltd\OpenQuant 2014\XAPI_CSharp.dll
+
+
+
+
+
+
+
+
diff --git a/QuantBox.APIProvider/QuantBox.APIProvider.csproj.user b/QuantBox.APIProvider/QuantBox.APIProvider.csproj.user
new file mode 100644
index 0000000..34a18ad
--- /dev/null
+++ b/QuantBox.APIProvider/QuantBox.APIProvider.csproj.user
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Form
+
+
+ Form
+
+
+
\ No newline at end of file
diff --git a/QuantBox.API.Provider/Single/ApiItem.cs b/QuantBox.APIProvider/Single/ApiItem.cs
similarity index 88%
rename from QuantBox.API.Provider/Single/ApiItem.cs
rename to QuantBox.APIProvider/Single/ApiItem.cs
index 37b7545..10c5cca 100644
--- a/QuantBox.API.Provider/Single/ApiItem.cs
+++ b/QuantBox.APIProvider/Single/ApiItem.cs
@@ -1,18 +1,13 @@
-using Newtonsoft.Json;
-using QuantBox.APIProvider.UI;
+using QuantBox.APIProvider.UI;
using System;
-using System.Collections.Generic;
using System.ComponentModel;
-using System.ComponentModel.Design;
-using System.Drawing.Design;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-using System.Threading.Tasks;
using XAPI;
namespace QuantBox.APIProvider.Single
{
+#if NET48
+ using System.Drawing.Design;
+#endif
///
/// 由用户选择Dll,然后加载,得到
///
@@ -28,9 +23,9 @@ public class ApiItem : ICloneable
private string _dllPath;
private string _typeName;
- private IXApi CheckApi(string typeName,string dllPath)
+ private IXApi CheckApi(string typeName, string dllPath)
{
- if(string.IsNullOrEmpty(typeName))
+ if (string.IsNullOrEmpty(typeName))
{
TypeName = "XAPI.Callback.XApi, XAPI_CSharp";
return null;
@@ -55,8 +50,9 @@ private IXApi CheckApi(string typeName,string dllPath)
}
return api;
}
-
+#if NET48
[Editor(typeof(System.Windows.Forms.Design.FileNameEditor), typeof(System.Drawing.Design.UITypeEditor))]
+#endif
public string DllPath
{
get
@@ -76,7 +72,8 @@ public string TypeName
{
return _typeName;
}
- set {
+ set
+ {
_typeName = value;
Api = CheckApi(_typeName, _dllPath);
}
@@ -104,8 +101,10 @@ public string TypeName
[Category(CATEGORY_TYPE)]
[ReadOnly(true)]
public ApiType Type { get; set; }
- [Category(CATEGORY_TYPE)]
+#if NET48
[Editor(typeof(ApiTypeSelectorEditor), typeof(UITypeEditor))]
+#endif
+ [Category(CATEGORY_TYPE)]
public ApiType UseType { get; set; }
diff --git a/QuantBox.API.Provider/Single/BaseMap.cs b/QuantBox.APIProvider/Single/BaseMap.cs
similarity index 72%
rename from QuantBox.API.Provider/Single/BaseMap.cs
rename to QuantBox.APIProvider/Single/BaseMap.cs
index 7f045b5..4c54317 100644
--- a/QuantBox.API.Provider/Single/BaseMap.cs
+++ b/QuantBox.APIProvider/Single/BaseMap.cs
@@ -12,7 +12,7 @@ namespace QuantBox.APIProvider.Single
{
class BaseMap
{
- private Framework framework;
+ protected Framework framework;
protected SingleProvider provider;
public BaseMap(Framework framework, SingleProvider provider)
@@ -31,17 +31,6 @@ public ExecutionReport CreateReport(
report.DateTime = framework.Clock.DateTime;
- //report.Order = record.Order;
- //report.Instrument = record.Order.Instrument;
-
- //report.Side = record.Order.Side;
- //report.OrdType = record.Order.Type;
- //report.TimeInForce = record.Order.TimeInForce;
-
- //report.OrdQty = record.Order.Qty;
- //report.Price = record.Order.Price;
- //report.StopPx = record.Order.StopPx;
-
report.AvgPx = record.AvgPx;
report.CumQty = record.CumQty;
report.LeavesQty = record.LeavesQty;
@@ -55,6 +44,28 @@ public ExecutionReport CreateReport(
return report;
}
+ public ExecutionReport CreateReport(
+ Order order,
+ SQ.ExecType? execType,
+ SQ.OrderStatus? orderStatus)
+ {
+ ExecutionReport report = new ExecutionReport(order);
+
+ report.DateTime = framework.Clock.DateTime;
+
+ report.AvgPx = order.AvgPx;
+ report.CumQty = order.CumQty;
+ report.LeavesQty = order.LeavesQty;
+
+ if (execType != null)
+ report.ExecType = execType.Value;
+
+ if (orderStatus != null)
+ report.OrdStatus = orderStatus.Value;
+
+ return report;
+ }
+
public void EmitExecutionReport(OrderRecord record, SQ.ExecType execType, SQ.OrderStatus orderStatus)
{
ExecutionReport report = CreateReport(record, execType, orderStatus);
diff --git a/QuantBox.API.Provider/Single/Extensions.cs b/QuantBox.APIProvider/Single/Extensions.cs
similarity index 100%
rename from QuantBox.API.Provider/Single/Extensions.cs
rename to QuantBox.APIProvider/Single/Extensions.cs
diff --git a/QuantBox.API.Provider/Single/ExternalOrderRecord.cs b/QuantBox.APIProvider/Single/ExternalOrderRecord.cs
similarity index 100%
rename from QuantBox.API.Provider/Single/ExternalOrderRecord.cs
rename to QuantBox.APIProvider/Single/ExternalOrderRecord.cs
diff --git a/QuantBox.API.Provider/Single/HistoricalDataRecord.cs b/QuantBox.APIProvider/Single/HistoricalDataRecord.cs
similarity index 100%
rename from QuantBox.API.Provider/Single/HistoricalDataRecord.cs
rename to QuantBox.APIProvider/Single/HistoricalDataRecord.cs
diff --git a/QuantBox.API.Provider/Single/InstrumentJson.cs b/QuantBox.APIProvider/Single/InstrumentJson.cs
similarity index 100%
rename from QuantBox.API.Provider/Single/InstrumentJson.cs
rename to QuantBox.APIProvider/Single/InstrumentJson.cs
diff --git a/QuantBox.API.Provider/Single/MarketDataRecord.cs b/QuantBox.APIProvider/Single/MarketDataRecord.cs
similarity index 100%
rename from QuantBox.API.Provider/Single/MarketDataRecord.cs
rename to QuantBox.APIProvider/Single/MarketDataRecord.cs
diff --git a/QuantBox.API.Provider/Single/NoTypeConverterJsonConverter.cs b/QuantBox.APIProvider/Single/NoTypeConverterJsonConverter.cs
similarity index 100%
rename from QuantBox.API.Provider/Single/NoTypeConverterJsonConverter.cs
rename to QuantBox.APIProvider/Single/NoTypeConverterJsonConverter.cs
diff --git a/QuantBox.API.Provider/Single/OrderMap.cs b/QuantBox.APIProvider/Single/OrderMap.cs
similarity index 90%
rename from QuantBox.API.Provider/Single/OrderMap.cs
rename to QuantBox.APIProvider/Single/OrderMap.cs
index 8e8f7b5..58d3e83 100644
--- a/QuantBox.API.Provider/Single/OrderMap.cs
+++ b/QuantBox.APIProvider/Single/OrderMap.cs
@@ -228,7 +228,7 @@ public void Process(ref OrderField order, NLog.Logger log)
workingOrders.Remove(order.ID);
orderIDs.Remove(record.Order.Id);
record.LeavesQty = 0;
- EmitExecutionReport(record, SQ.ExecType.ExecCancelled, SQ.OrderStatus.Cancelled);
+ EmitExecutionReport(record, SQ.ExecType.ExecCancelled, SQ.OrderStatus.Cancelled, order.Text());
}
else if (this.pendingOrders.TryRemove(order.LocalID, out record))
{
@@ -318,6 +318,46 @@ record = GetExternalOrder(ref trade);
}
}
+ public void ProcessExpired(ref InstrumentStatusField instrumentStatus, NLog.Logger log)
+ {
+ // 根据当前挂单来统计
+ //if (workingOrders.Count == 0)
+ // return;
+
+ if (instrumentStatus.InstrumentStatus != TradingPhaseType.Closed)
+ return;
+
+ foreach (var order in framework.OrderManager.Orders)
+ {
+ // 量有点多,需要判断一下
+ if (order.IsDone)
+ continue;
+
+ var inst = order.Instrument;
+
+ string altSymbol;
+ string altExchange;
+ string apiSymbol;
+ string apiExchange;
+ double apiTickSize;
+ string apiProductID;
+
+ this.provider.GetApi_Symbol_Exchange_TickSize(inst, this.provider.Id,
+ out altSymbol, out altExchange,
+ out apiSymbol, out apiExchange,
+ out apiTickSize,
+ out apiProductID);
+
+ if (apiProductID != instrumentStatus.InstrumentID)
+ continue;
+
+ ExecutionReport report = CreateReport(order, SQ.ExecType.ExecExpired, SQ.OrderStatus.Expired);
+ report.LeavesQty = 0;
+ report.Text = "收盘,插件标记定单过期";
+ provider.EmitExecutionReport(report);
+ }
+ }
+
public void ProcessNew(ref QuoteField quote, QuoteRecord record)
{
OrderRecord askRecord = new OrderRecord(record.AskOrder);
diff --git a/QuantBox.API.Provider/Single/OrderRecord.cs b/QuantBox.APIProvider/Single/OrderRecord.cs
similarity index 100%
rename from QuantBox.API.Provider/Single/OrderRecord.cs
rename to QuantBox.APIProvider/Single/OrderRecord.cs
diff --git a/QuantBox.API.Provider/Single/QuoteMap.cs b/QuantBox.APIProvider/Single/QuoteMap.cs
similarity index 100%
rename from QuantBox.API.Provider/Single/QuoteMap.cs
rename to QuantBox.APIProvider/Single/QuoteMap.cs
diff --git a/QuantBox.API.Provider/Single/QuoteRecord.cs b/QuantBox.APIProvider/Single/QuoteRecord.cs
similarity index 100%
rename from QuantBox.API.Provider/Single/QuoteRecord.cs
rename to QuantBox.APIProvider/Single/QuoteRecord.cs
diff --git a/QuantBox.APIProvider/Single/ServerItem.cs b/QuantBox.APIProvider/Single/ServerItem.cs
new file mode 100644
index 0000000..2ba7078
--- /dev/null
+++ b/QuantBox.APIProvider/Single/ServerItem.cs
@@ -0,0 +1,36 @@
+using XAPI;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+#if NET48
+using System.Drawing.Design;
+#endif
+
+namespace QuantBox.APIProvider.Single
+
+{
+ [DefaultProperty("Label")]
+ public class ServerItem : ICloneable
+ {
+ [Category("标签")]
+ public string Label { get; set; }
+
+#if NET48
+ [Editor(typeof(System.Windows.Forms.Design.FileNameEditor), typeof(UITypeEditor))]
+#endif
+ public string Path { get; set; }
+
+ public override string ToString()
+ {
+ return string.Format("Label={0}", this.Label);
+ }
+
+ public object Clone()
+ {
+ return this.MemberwiseClone();
+ }
+ }
+}
diff --git a/QuantBox.APIProvider/Single/SessionTimeItem.cs b/QuantBox.APIProvider/Single/SessionTimeItem.cs
new file mode 100644
index 0000000..ff5bbeb
--- /dev/null
+++ b/QuantBox.APIProvider/Single/SessionTimeItem.cs
@@ -0,0 +1,105 @@
+using Newtonsoft.Json;
+using OrderedPropertyGrid;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace QuantBox.APIProvider.Single
+{
+ // 发现想让属性显示排序,但发现Json转换出问题了,所以还是决定不排序了
+ [TypeConverter(typeof(PropertySorter))]
+ [JsonConverter(typeof(NoTypeConverterJsonConverter))]
+ public class SessionTimeItem
+ {
+ public const string CATEGORY_DAY_OF_WEEK = "DayOfWeek";
+
+ private static string[] DayOfWeekChinese = new string[] { "日", "一", "二", "三", "四", "五", "六" };
+
+ private List DayOfWeekList = null;
+
+ [PropertyOrder(1)]
+ public TimeSpan SessionStart { get; set; }
+ [PropertyOrder(2)]
+ public TimeSpan SessionEnd { get; set; }
+
+ [Category(CATEGORY_DAY_OF_WEEK)]
+ [PropertyOrder(1)]
+ public bool Sunday { get; set; }
+ [Category(CATEGORY_DAY_OF_WEEK)]
+ [PropertyOrder(2)]
+ public bool Monday { get; set; }
+ [Category(CATEGORY_DAY_OF_WEEK)]
+ [PropertyOrder(3)]
+ public bool Tuesday { get; set; }
+ [Category(CATEGORY_DAY_OF_WEEK)]
+ [PropertyOrder(4)]
+ public bool Wednesday { get; set; }
+ [Category(CATEGORY_DAY_OF_WEEK)]
+ [PropertyOrder(5)]
+ public bool Thursday { get; set; }
+ [Category(CATEGORY_DAY_OF_WEEK)]
+ [PropertyOrder(6)]
+ public bool Friday { get; set; }
+ [Category(CATEGORY_DAY_OF_WEEK)]
+ [PropertyOrder(7)]
+ public bool Saturday { get; set; }
+
+ public bool Contains(DayOfWeek dayOfWeek)
+ {
+ if (DayOfWeekList == null)
+ DayOfWeekList = GetDayOfWeekList();
+
+ return DayOfWeekList.Contains(dayOfWeek);
+ }
+
+ public List GetDayOfWeekList()
+ {
+ var list = new List();
+ if (Sunday) list.Add(DayOfWeek.Sunday);
+ if (Monday) list.Add(DayOfWeek.Monday);
+ if (Tuesday) list.Add(DayOfWeek.Tuesday);
+ if (Wednesday) list.Add(DayOfWeek.Wednesday);
+ if (Thursday) list.Add(DayOfWeek.Thursday);
+ if (Friday) list.Add(DayOfWeek.Friday);
+ if (Saturday) list.Add(DayOfWeek.Saturday);
+
+ DayOfWeekList = list;
+
+ return list;
+ }
+
+ public string GetDayOfWeekString()
+ {
+ var strs = new List();
+ var list = GetDayOfWeekList();
+ foreach (var l in list)
+ {
+ strs.Add(DayOfWeekChinese[Convert.ToInt16(l)]);
+ }
+ return string.Join("", strs);
+ }
+
+
+ public override string ToString()
+ {
+ var list = new List() { SessionStart, SessionEnd };
+ var strs = new List();
+ foreach (var ts in list)
+ {
+ if (ts.TotalDays >= 1.0)
+ {
+ strs.Add(ts.ToString(@"d\:mm"));
+ }
+ else
+ {
+ strs.Add(ts.ToString(@"hh\:mm"));
+ }
+
+ }
+ return $"[{string.Join(",", strs)}] {{{GetDayOfWeekString()}}}";
+ }
+ }
+}
diff --git a/QuantBox.API.Provider/Single/SingleProvider.API.Connection.cs b/QuantBox.APIProvider/Single/SingleProvider.API.Connection.cs
similarity index 78%
rename from QuantBox.API.Provider/Single/SingleProvider.API.Connection.cs
rename to QuantBox.APIProvider/Single/SingleProvider.API.Connection.cs
index ccefa43..11dbf5d 100644
--- a/QuantBox.API.Provider/Single/SingleProvider.API.Connection.cs
+++ b/QuantBox.APIProvider/Single/SingleProvider.API.Connection.cs
@@ -1,14 +1,12 @@
using SmartQuant;
using System;
-using System.Collections.Generic;
using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Forms;
using XAPI;
using NLog;
using QuantBox.Extensions;
+using System.Threading;
+using System.IO;
namespace QuantBox.APIProvider.Single
{
@@ -133,85 +131,91 @@ private void _Disconnect(bool bFromUI)
}
- private int nDisconnectCount = 0;
- void _Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
+ private void CheckConnection(System.Timers.ElapsedEventArgs e)
{
- lock (this)
+ do
{
- _Timer.Enabled = false;
+ // 列表为空,表示不处理。这时没有自动重连
+ if (SessionTimeList == null || SessionTimeList.Count == 0)
+ break;
- do
+ var stl = SessionTimeList.Where(x => x.Contains(e.SignalTime.DayOfWeek)).ToList();
+ if (stl.Count == 0)
{
- // 列表为空,表示不处理。这时没有自动重连
- if (SessionTimeList == null || SessionTimeList.Count == 0)
- break;
-
- var stl = SessionTimeList.Where(x => x.Enable).ToList();
- if (stl.Count == 0)
- break;
+ break;
+ }
- bool bTryConnect = true;
+ bool bTryConnect = true;
+ TimeSpan ts = e.SignalTime.TimeOfDay;
+ SessionTimeItem st_current = null;
+ SessionTimeItem st_next = null;
+ foreach (var st in stl)
+ {
+ // 如果当前时间在交易范围内,要开启重连
+ // 如果当前时间不在交易范围内,要主动断开
- SessionTimeItem st_current = null;
- SessionTimeItem st_next = null;
- foreach (var st in stl)
+ if (ts < st.SessionStart)
+ {
+ // 停
+ bTryConnect = false;
+ st_next = st;
+ break;
+ }
+ else if (ts <= st.SessionEnd)
+ {
+ // 启动
+ bTryConnect = true;
+ st_current = st;
+ break;
+ }
+ else
{
- // 如果当前时间在交易范围内,要开启重连
- // 如果当前时间不在交易范围内,要主动断开
- TimeSpan ts = e.SignalTime.TimeOfDay;
- if (!st.Enable)
- continue;
-
- if (ts < st.SessionStart)
- {
- // 停
- bTryConnect = false;
- st_next = st;
- }
- else if (ts <= st.SessionEnd)
- {
- // 启动
- bTryConnect = true;
- st_current = st;
- break;
- }
- else
- {
- // 停
- bTryConnect = false;
- st_next = st;
- }
+ // 停
+ bTryConnect = false;
+ st_next = st;
}
+ }
- if (bTryConnect)
+ if (bTryConnect)
+ {
+ // 没有连接要连上,有连接要设置时间
+ if (!IsConnected)
{
- // 没有连接要连上,有连接要设置时间
- if (!IsConnected)
- {
- xlog.Info("当前[{0}]在交易时段[{1}],主动连接", e.SignalTime.TimeOfDay, st_current);
- _Connect(false);
- }
+ xlog.Info($"当前[{e.SignalTime.TimeOfDay}]在交易时段[{st_current}],主动连接");
+ _Connect(false);
+ }
- // 初始化查询间隔
- SetApiReconnectInterval(_ReconnectInterval);
+ // 初始化查询间隔
+ SetApiReconnectInterval(_ReconnectInterval);
- nDisconnectCount = 0;
- }
- else
+ nDisconnectCount = 0;
+ }
+ else
+ {
+ // 关闭查询间隔
+ SetApiReconnectInterval(0);
+ // 由于定时器设置的是20秒,所以这里正好是5分钟显示一次
+ if (nDisconnectCount % (3 * 5) == 0)
{
- // 关闭查询间隔
- SetApiReconnectInterval(0);
- // 由于定时器设置的是20秒,所以这里正好是5分钟显示一次
- if (nDisconnectCount % (3 * 5) == 0)
- {
- xlog.Info("当前[{0}]在非交易时段,主动断开连接,下次要连接的时段为[{1}]", e.SignalTime.TimeOfDay, st_next);
-
- // 要断开连接
- _Disconnect(false);
- }
- ++nDisconnectCount;
+ xlog.Info($"当前[{e.SignalTime.TimeOfDay}]在非交易时段,主动断开连接,下次要连接的时段为[{st_next}](仅限当日)");
+
+ // 要断开连接
+ _Disconnect(false);
}
- } while (false);
+ ++nDisconnectCount;
+ }
+ } while (false);
+ }
+
+
+ private int nDisconnectCount = 0;
+ void _Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
+ {
+ lock (this)
+ {
+ _Timer.Enabled = false;
+
+ CheckConnection(e);
// 查询持仓和资金
QueryAccountPosition_OnTimer();
@@ -276,8 +280,10 @@ private void OnConnectionStatus_Done(object sender, ConnectionStatus status)
// 每个连接都检查是否连上,如果连上,开始进行一些基本的查询
if (bCheckOk)
{
- base.Status = ProviderStatus.Connected;
+ // 晚一点通知上层,这个更稳定一些
+ // base.Status = ProviderStatus.Connected;
+ // 这个查询不能太快,否则CTP报错
QueryAccountPositionInstrument_Logined();
}
}
@@ -375,11 +381,12 @@ private IXApi ConnectToApi(ApiItem item)
item.Api = api;
}
- api.Server = ServerList[item.Server].ToStruct();
- api.User = UserList[item.User].ToStruct();
+ string szServerPath = ServerList[item.Server].Path;
+ string szUserPath = UserList[item.User].Path;
+ string szUserLabel = UserList[item.User].Label;
// 更新Log名字,这样在日志中可以进行识别
- api.Log = LogManager.GetLogger(string.Format("{0}.{1}.{2}", Name, item.LogPrefix, api.User.UserID));
+ api.Log = LogManager.GetLogger(string.Format("{0}.{1}.{2}", Name, item.LogPrefix, szUserLabel));
if (api.IsConnected)
return api;
@@ -411,7 +418,7 @@ private IXApi ConnectToApi(ApiItem item)
api.OnRspQrySettlementInfo = OnRspQrySettlementInfo_callback;
api.OnRtnInstrumentStatus = OnRtnInstrumentStatus_callback;
- api.Connect();
+ api.Connect(szServerPath, szUserPath, Path.GetTempPath());
return api;
}
@@ -425,10 +432,8 @@ private void DisconnectToApi(ApiItem item)
// 直接销毁
_DisconnectToApi(item.Api);
- //// 在线程中销毁
- //Task task = Task.Factory.StartNew(
- // ()=> { _DisconnectToApi(item.Api); }
- // );
+ // 立即断开连接后,登录时自动查资金与持仓的功能在线程中还在运行,会报错
+ assign(item, null);
item.Api = null;
}
@@ -518,6 +523,13 @@ private void OnRtnQuoteRequest_callback(object sender, ref QuoteRequestField quo
}
private void QueryAccountPositionInstrument_Logined()
+ {
+ // OnRtnError:[XErrorID = 0; RawErrorID = 90; Text = CTP:查询未就绪,请稍后重试; Source = OnRspError]
+ Thread thread2 = new Thread(QueryAccountPositionInstrument_Thread);
+ thread2.Start();
+ }
+
+ private void QueryAccountPositionInstrument_Thread()
{
ReqQueryField query = new ReqQueryField();
query.PortfolioID1 = DefaultPortfolioID1;
@@ -525,17 +537,30 @@ private void QueryAccountPositionInstrument_Logined()
query.PortfolioID3 = DefaultPortfolioID3;
query.Business = DefaultBusiness;
+
+ Thread.Sleep(3000);
+ // 查合约
+ if (IsApiConnected(_ItApi))
+ {
+ _ItApi.ReqQuery(QueryType.ReqQryInstrument, query);
+ }
+
// 查持仓,查资金
+ Thread.Sleep(3000);
if (IsApiConnected(_QueryApi))
{
- _QueryApi.ReqQuery(QueryType.ReqQryTradingAccount, query);
+ _dictPositions_current.Clear();
_QueryApi.ReqQuery(QueryType.ReqQryInvestorPosition, query);
}
- // 查合约
- if (IsApiConnected(_ItApi))
+ // 晚一点通知上层会不会更稳定一些?
+ base.Status = ProviderStatus.Connected;
+
+ Thread.Sleep(3000);
+ if (IsApiConnected(_QueryApi))
{
- _ItApi.ReqQuery(QueryType.ReqQryInstrument, query);
+ _dictAccounts_current.Clear();
+ _QueryApi.ReqQuery(QueryType.ReqQryTradingAccount, query);
}
}
@@ -550,19 +575,21 @@ private void QueryAccountPosition_OnTimer()
query.PortfolioID3 = DefaultPortfolioID3;
query.Business = DefaultBusiness;
- _QueryAccountCount -= (int)_Timer.Interval / 1000;
- if (_QueryAccountCount <= 0)
- {
- _QueryApi.ReqQuery(QueryType.ReqQryTradingAccount, query);
- _QueryAccountCount = _QueryAccountInterval;
- }
-
_QueryPositionCount -= (int)_Timer.Interval / 1000;
if (_QueryPositionCount <= 0)
{
+ _dictPositions_current.Clear();
_QueryApi.ReqQuery(QueryType.ReqQryInvestorPosition, query);
_QueryPositionCount = _QueryPositionInterval;
}
+
+ _QueryAccountCount -= (int)_Timer.Interval / 1000;
+ if (_QueryAccountCount <= 0)
+ {
+ _dictAccounts_current.Clear();
+ _QueryApi.ReqQuery(QueryType.ReqQryTradingAccount, query);
+ _QueryAccountCount = _QueryAccountInterval;
+ }
}
#endregion
diff --git a/QuantBox.API.Provider/Single/SingleProvider.API.HistoricalData.cs b/QuantBox.APIProvider/Single/SingleProvider.API.HistoricalData.cs
similarity index 99%
rename from QuantBox.API.Provider/Single/SingleProvider.API.HistoricalData.cs
rename to QuantBox.APIProvider/Single/SingleProvider.API.HistoricalData.cs
index 987ed00..076118b 100644
--- a/QuantBox.API.Provider/Single/SingleProvider.API.HistoricalData.cs
+++ b/QuantBox.APIProvider/Single/SingleProvider.API.HistoricalData.cs
@@ -155,7 +155,7 @@ private void OnRspQryHistoricalTicks_callback(object sender, IntPtr pTicks, int
volume = obj.Volume;
}
- if(EmitHistoricalData)
+ if(EnablEmitHistoricalData)
{
HistoricalData data = new HistoricalData
{
@@ -217,7 +217,7 @@ private void OnRspQryHistoricalBars_callback(object sender, IntPtr pBars, int si
}
}
- if(EmitHistoricalData)
+ if(EnablEmitHistoricalData)
{
HistoricalData data = new HistoricalData
{
diff --git a/QuantBox.API.Provider/Single/SingleProvider.API.MarketData.cs b/QuantBox.APIProvider/Single/SingleProvider.API.MarketData.cs
similarity index 100%
rename from QuantBox.API.Provider/Single/SingleProvider.API.MarketData.cs
rename to QuantBox.APIProvider/Single/SingleProvider.API.MarketData.cs
diff --git a/QuantBox.API.Provider/Single/SingleProvider.API.Order.cs b/QuantBox.APIProvider/Single/SingleProvider.API.Order.cs
similarity index 91%
rename from QuantBox.API.Provider/Single/SingleProvider.API.Order.cs
rename to QuantBox.APIProvider/Single/SingleProvider.API.Order.cs
index 875f36a..81f111d 100644
--- a/QuantBox.API.Provider/Single/SingleProvider.API.Order.cs
+++ b/QuantBox.APIProvider/Single/SingleProvider.API.Order.cs
@@ -1,15 +1,10 @@
using SmartQuant;
using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Forms;
using QuantBox.Extensions;
using XAPI.Callback;
using XAPI;
-using Newtonsoft.Json;
using SQ = SmartQuant;
@@ -143,11 +138,13 @@ private void CmdNewOrderSingle(ExecutionCommand command)
string apiSymbol;
string apiExchange;
double apiTickSize;
+ string apiProductID;
GetApi_Symbol_Exchange_TickSize(command.Instrument, this.id,
out altSymbol, out altExchange,
out apiSymbol, out apiExchange,
- out apiTickSize);
+ out apiTickSize,
+ out apiProductID);
OrderField[] fields = new OrderField[1];
@@ -206,11 +203,13 @@ private void CmdNewOrderList(ExecutionCommand command)
string apiSymbol;
string apiExchange;
double apiTickSize;
+ string apiProductID;
- GetApi_Symbol_Exchange_TickSize(orders[i].Instrument, this.id,
+ GetApi_Symbol_Exchange_TickSize(command.Instrument, this.id,
out altSymbol, out altExchange,
out apiSymbol, out apiExchange,
- out apiTickSize);
+ out apiTickSize,
+ out apiProductID);
ToOrderStruct(ref fields[i], orders[i], apiSymbol, apiExchange);
}
@@ -267,7 +266,7 @@ private void OnRtnOrder_callback(object sender, ref OrderField order)
private void OnRtnTrade_callback(object sender, ref TradeField trade)
{
- lock(this)
+ lock (this)
{
var log = (sender as XApi).GetLog();
log.Debug("OnRtnTrade:" + trade.ToFormattedString());
@@ -286,5 +285,25 @@ private void OnRtnTrade_callback(object sender, ref TradeField trade)
}
}
}
+
+ private void OnRtnOrder_Expired(ref InstrumentStatusField instrumentStatus)
+ {
+ if (_TdApi == null)
+ return;
+
+ lock (this)
+ {
+ var log = _TdApi.GetLog();
+
+ try
+ {
+ orderMap.ProcessExpired(ref instrumentStatus, log);
+ }
+ catch (Exception ex)
+ {
+ log.Error(ex);
+ }
+ }
+ }
}
}
diff --git a/QuantBox.API.Provider/Single/SingleProvider.API.Quote.cs b/QuantBox.APIProvider/Single/SingleProvider.API.Quote.cs
similarity index 90%
rename from QuantBox.API.Provider/Single/SingleProvider.API.Quote.cs
rename to QuantBox.APIProvider/Single/SingleProvider.API.Quote.cs
index febe93f..542d95e 100644
--- a/QuantBox.API.Provider/Single/SingleProvider.API.Quote.cs
+++ b/QuantBox.APIProvider/Single/SingleProvider.API.Quote.cs
@@ -1,13 +1,6 @@
-using Newtonsoft.Json;
-using NLog;
-using QuantBox.Extensions;
+using QuantBox.Extensions;
using SmartQuant;
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Forms;
using XAPI;
using XAPI.Callback;
@@ -47,11 +40,13 @@ private void CmdNewQuote(ExecutionCommand command)
string apiSymbol;
string apiExchange;
double apiTickSize;
+ string apiProductID;
GetApi_Symbol_Exchange_TickSize(command.Instrument, this.id,
out altSymbol, out altExchange,
out apiSymbol, out apiExchange,
- out apiTickSize);
+ out apiTickSize,
+ out apiProductID);
Order bidOrder;
Order askOrder;
diff --git a/QuantBox.APIProvider/Single/SingleProvider.API.cs b/QuantBox.APIProvider/Single/SingleProvider.API.cs
new file mode 100644
index 0000000..06a3e48
--- /dev/null
+++ b/QuantBox.APIProvider/Single/SingleProvider.API.cs
@@ -0,0 +1,425 @@
+using SmartQuant;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using XAPI.Callback;
+using XAPI;
+using NLog;
+using System.Reflection;
+using QuantBox.Extensions;
+using System.IO;
+
+namespace QuantBox.APIProvider.Single
+{
+ public partial class SingleProvider
+ {
+ static SingleProvider()
+ {
+ NLog.LogManager.LoadConfiguration(Path.Combine(PathHelper.RootPath.LocalPath, "NLog.config"));
+ }
+ public DelegateOnRspQryInvestorPosition OnRspQryInvestorPosition { get; set; }
+ public DelegateOnRspQryTradingAccount OnRspQryTradingAccount { get; set; }
+ public DelegateOnRtnInstrumentStatus OnRtnInstrumentStatus { get; set; }
+ public DelegateOnRtnOrder OnRtnOrder { get; set; }
+ public DelegateOnRtnTrade OnRtnTrade { get; set; }
+
+ public DelegateOnRspQryOrder OnRspQryOrder { get; set; }
+ public DelegateOnRspQryTrade OnRspQryTrade { get; set; }
+
+ //记录合约列表,从实盘合约名到对象的映射
+ private readonly Dictionary _dictInstruments = new Dictionary();
+ // 品种交易状态
+ private readonly Dictionary _dictInstrumentsStatus = new Dictionary();
+
+ // 账号信息
+ private SortedDictionary _dictAccounts_current = new SortedDictionary();
+ private SortedDictionary _dictAccounts_last = new SortedDictionary();
+
+ // 持仓信息
+ private SortedDictionary _dictPositions_current = new SortedDictionary();
+ private SortedDictionary _dictPositions_last = new SortedDictionary();
+
+ public static int GetDate(DateTime dt)
+ {
+ return dt.Year * 10000 + dt.Month * 100 + dt.Day;
+ }
+
+ public static int GetTime(DateTime dt)
+ {
+ return dt.Hour * 10000 + dt.Minute * 100 + dt.Second;
+ }
+
+ public static DateTime GetDateTime(int yyyyMMdd, int hhmmss, int Millisecond)
+ {
+ int yyyy = yyyyMMdd / 10000;
+ int MM = yyyyMMdd % 10000 / 100;
+ int dd = yyyyMMdd % 100;
+ int hh = hhmmss / 10000;
+ int mm = hhmmss % 10000 / 100;
+ int ss = hhmmss % 100;
+ DateTime dt = new DateTime(yyyy, MM, dd, hh, mm, ss, Millisecond);
+ return dt;
+ }
+
+ private void OnRspQryInstrument_callback(object sender, ref InstrumentField instrument, int size1, bool bIsLast)
+ {
+ if (size1 <= 0)
+ {
+ (sender as XApi).GetLog().Info("OnRspQryInstrument");
+ return;
+ }
+
+ _dictInstruments[instrument.Symbol] = instrument;
+
+ if (bIsLast)
+ {
+ (sender as XApi).GetLog().Info("合约列表已经接收完成,共 {0} 条", _dictInstruments.Count);
+ }
+ }
+
+ private void OnRspQryTradingAccount_callback(object sender, ref AccountField account, int size1, bool bIsLast)
+ {
+ if (size1 <= 0)
+ {
+ (sender as XApi).GetLog().Info("OnRspQryTradingAccount");
+ }
+ else
+ {
+ if (IsLogOnRspQryTradingAccount)
+ (sender as XApi).GetLog().Info("OnRspQryTradingAccount:" + account.ToFormattedString());
+ }
+
+ // 由策略来收回报
+ if (OnRspQryTradingAccount != null)
+ OnRspQryTradingAccount(sender, ref account, size1, bIsLast);
+
+ if (size1 > 0)
+ {
+ _dictAccounts_current[account.AccountID] = account;
+ }
+
+ if (!bIsLast)
+ return;
+
+ var list = MergeAccounts(_dictAccounts_current, _dictAccounts_last);
+ alog.Info(string.Join("\n", list));
+
+ _dictAccounts_last = _dictAccounts_current;
+ _dictAccounts_current = new SortedDictionary();
+
+ if (!IsConnected)
+ return;
+
+ foreach (var acc in _dictAccounts_last.Values)
+ {
+ string currency = "CNY";
+
+ AccountData ad = new AccountData(DateTime.Now, AccountDataType.AccountValue,
+ acc.AccountID, this.id, this.id);
+
+ Type type = typeof(AccountField);
+ FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
+ foreach (FieldInfo field in fields)
+ {
+ ad.Fields.Add(field.Name, currency, field.GetValue(acc));
+ }
+ // 将对像完全设置进去,等着取出
+ ad.Fields.Add(AccountDataFieldEx.USER_DATA, currency, acc);
+ ad.Fields.Add(AccountDataFieldEx.DATE, currency, GetDate(DateTime.Today));
+
+ try
+ {
+ EmitAccountData(ad);
+ }
+ catch (Exception ex)
+ {
+ (sender as XApi).GetLog().Error(ex);
+ }
+ }
+ }
+
+ private List MergeAccounts(SortedDictionary dict_curr, SortedDictionary dict_last)
+ {
+ var list = new List();
+ // 交集
+ {
+ var keys = dict_curr.Keys.Intersect(dict_last.Keys);
+ foreach (var key in keys)
+ {
+ var curr = dict_curr[key];
+ var last = dict_last[key];
+
+ list.Add(AccountMsg_Long(curr, last));
+ }
+ }
+
+ // 差集,表示新增
+ {
+ var keys = dict_curr.Keys.Except(dict_last.Keys);
+ foreach (var key in keys)
+ {
+ var curr = dict_curr[key];
+ var last = new AccountField();
+
+ list.Add(AccountMsg_Long(curr, last));
+ }
+ }
+ return list;
+
+ }
+
+ private void OnRspQryInvestor_callback(object sender, ref InvestorField investor, int size1, bool bIsLast)
+ {
+ if (size1 <= 0)
+ {
+ (sender as XApi).GetLog().Info("OnRspQryInvestor");
+ return;
+ }
+
+ (sender as XApi).GetLog().Info("OnRspQryInvestor:{0}", investor.ToFormattedString());
+ }
+
+ private void OnRspQryInvestorPosition_callback(object sender, ref PositionField position, int size1, bool bIsLast)
+ {
+ if (size1 <= 0)
+ {
+ (sender as XApi).GetLog().Info("OnRspQryInvestorPosition");
+ }
+ else if (position.Position != 0)
+ {
+ // UFX中已经过期的持仓也会推送,所以这里过滤一下不显示
+ if (IsLogOnRspQryInvestorPosition)
+ (sender as XApi).GetLog().Info("OnRspQryInvestorPosition:" + position.ToFormattedString());
+ }
+
+ // 由策略来收回报
+ if (OnRspQryInvestorPosition != null)
+ OnRspQryInvestorPosition(sender, ref position, size1, bIsLast);
+
+ // 需要保证从上一个查询到现在,列表中的数据是本次查询的所有数据
+ if (size1 > 0)
+ {
+ _dictPositions_current[position.ID] = position;
+ }
+
+ if (!bIsLast)
+ return;
+
+ PositionsMsg_Long(_dictPositions_current);
+
+ // 比较两次容器的区别
+ var list = MergePositions(_dictPositions_current, _dictPositions_last);
+ _dictPositions_last = _dictPositions_current;
+ _dictPositions_current = new SortedDictionary();
+
+ if (!IsConnected)
+ return;
+
+ // 没有持仓通知的合约,也通知为0
+ foreach (var pos in list)
+ {
+ PositionFieldEx item;
+ if (!positions.TryGetValue(pos.Symbol, out item))
+ {
+ item = new PositionFieldEx();
+ positions[pos.Symbol] = item;
+ }
+ item.AddPosition(pos);
+
+ AccountData ad = new AccountData(DateTime.Now, AccountDataType.Position,
+ pos.AccountID, this.id, this.id);
+
+ ad.Fields.Add(AccountDataField.SYMBOL, item.Symbol);
+ ad.Fields.Add(AccountDataField.EXCHANGE, item.Exchange);
+ ad.Fields.Add(AccountDataField.QTY, item.Qty);
+ ad.Fields.Add(AccountDataField.LONG_QTY, item.LongQty);
+ ad.Fields.Add(AccountDataField.SHORT_QTY, item.ShortQty);
+
+ ad.Fields.Add(AccountDataFieldEx.USER_DATA, item);
+ ad.Fields.Add(AccountDataFieldEx.DATE, GetDate(DateTime.Today));
+
+ try
+ {
+ EmitAccountData(ad);
+ }
+ catch (Exception ex)
+ {
+ (sender as XApi).GetLog().Error(ex);
+ }
+ }
+ }
+
+ private List MergePositions(SortedDictionary dict_curr, SortedDictionary dict_last)
+ {
+ // 由于可能收不到接口推送的持仓已经清空的消息,所以只能使用两次进行比较
+ var list = new List();
+
+ // 新的全部添加
+ foreach (var v in dict_curr.Values)
+ {
+ list.Add(v);
+ }
+
+ // 老的需要更改数据为0
+ {
+ var keys = dict_last.Keys.Except(dict_curr.Keys);
+ foreach (var key in keys)
+ {
+ var last = dict_last[key];
+ // 修改时间
+ last.Position = 0;
+ list.Add(last);
+ }
+ }
+
+ return list;
+ }
+
+ private void OnRspQrySettlementInfo_callback(object sender, ref SettlementInfoClass settlementInfo, int size1, bool bIsLast)
+ {
+ if (size1 <= 0)
+ {
+ (sender as XApi).GetLog().Info("OnRspQrySettlementInfo");
+ return;
+ }
+
+ if (bIsLast)
+ {
+ (sender as XApi).GetLog().Info("OnRspQrySettlementInfo:" + Environment.NewLine + settlementInfo.Content);
+ }
+ }
+
+
+ private void OnRspQryQuote_callback(object sender, ref QuoteField quote, int size1, bool bIsLast)
+ {
+ if (size1 <= 0)
+ {
+ (sender as XApi).GetLog().Info("OnRspQryQuote");
+ return;
+ }
+
+ (sender as XApi).GetLog().Info("OnRspQryQuote:" + quote.ToFormattedString());
+ }
+
+ private void OnRspQryTrade_callback(object sender, ref TradeField trade, int size1, bool bIsLast)
+ {
+ if (size1 <= 0)
+ {
+ (sender as XApi).GetLog().Info("OnRspQryTrade");
+
+ }
+ else
+ {
+ (sender as XApi).GetLog().Info("OnRspQryTrade:" + trade.ToFormattedString());
+ }
+ if (OnRspQryTrade != null)
+ OnRspQryTrade(this, ref trade, size1, bIsLast);
+ }
+
+ private void OnRspQryOrder_callback(object sender, ref OrderField order, int size1, bool bIsLast)
+ {
+ if (size1 <= 0)
+ {
+ (sender as XApi).GetLog().Info("OnRspQryOrder");
+ }
+ else
+ {
+ (sender as XApi).GetLog().Info("OnRspQryOrder:" + order.ToFormattedString());
+ }
+ if (OnRspQryOrder != null)
+ OnRspQryOrder(this, ref order, size1, bIsLast);
+ }
+
+ private void OnRtnInstrumentStatus_callback(object sender, ref InstrumentStatusField instrumentStatus)
+ {
+ if (OnRtnInstrumentStatus != null)
+ OnRtnInstrumentStatus(sender, ref instrumentStatus);
+
+ // 记录下来,后期可能要用到
+ _dictInstrumentsStatus[instrumentStatus.Symbol] = instrumentStatus;
+
+ // 考虑收盘后,主动为一些合约标记成过期
+ OnRtnOrder_Expired(ref instrumentStatus);
+
+ // 合约状态信息太多了,也不关心,这里屏蔽显示
+ if (IsLogOnRtnInstrumentStatus)
+ (sender as XApi).GetLog().Info("OnRtnInstrumentStatus:" + instrumentStatus.ToFormattedString());
+ }
+
+ public InstrumentStatusField GetInstrumentStatus(string symbol)
+ {
+ InstrumentStatusField instrumentStatus;
+ if (_dictInstrumentsStatus.TryGetValue(symbol, out instrumentStatus))
+ {
+ return instrumentStatus;
+ }
+ return instrumentStatus;
+ }
+
+ private string AccountMsg_Long(AccountField current, AccountField last)
+ {
+ double risk = current.CurrMargin * 100.0 / current.Balance;
+ double balance_1 = (current.Balance - current.Deposit + current.Withdraw) - current.PreBalance;
+ double balance_2 = current.Balance - last.Balance;
+
+ string str = $"{risk:F2}%/{current.PositionProfit:F0}/{balance_1:F0}/{balance_2:F0}" +
+ $"\n{current.CloseProfit:F0}/{current.Commission:F0}/{current.Available:F0}" +
+ $"\n风险度/持仓盈亏/日间权益差/区间权益差" +
+ $"\n平仓盈亏/手续费/可用资金\n" +
+ $"\n{current.Withdraw:F0}/{current.Deposit:F0}" +
+ $"\n出/入金" +
+ $"\n" +
+ $"\n{current.Balance:F0}-*+*-{current.PreBalance:F0}=*" +
+ $"\n(动态权益-入金+出金)-昨结权益=日间权益差" +
+ $"\n动态权益-上期动态权益=区间权益差" +
+ $"\n" +
+ $"\n{current.CurrMargin:F0}/*=*" +
+ $"\n占用保证金/动态权益=风险度" +
+ $"\n" +
+ $"\n>>AccountID:{current.AccountID}<<" +
+ $"\n>>{DateTime.Now.ToLongTimeString()}<<";
+
+ return str;
+ }
+
+ private string AccountMsg_Short(AccountField current, AccountField last)
+ {
+ double risk = current.CurrMargin * 100.0 / current.Balance;
+ double balance_1 = (current.Balance - current.Deposit + current.Withdraw) - current.PreBalance;
+ double balance_2 = current.Balance - last.Balance;
+
+ string str = "";
+
+ str += string.Format("{0:F2}%/{1:F0}/{2:F0}/{3:F0}", risk, current.PositionProfit, balance_1, balance_2);
+ str += string.Format("\n{0:F0}/{1:F0}/{2:F0}", current.CloseProfit, current.Commission, current.Available);
+ str += string.Format("\n{0:F0}", current.Balance);
+ str += string.Format("\n风险度/持仓盈亏/日间权益差/区间权益差");
+ str += string.Format("\n平仓盈亏/手续费/可用资金");
+ str += string.Format("\n动态权益");
+ str += string.Format("\n>>AccountID:{0}<<", current.AccountID);
+
+ return str;
+ }
+
+ private string PositionsMsg_Long(SortedDictionary positions)
+ {
+ if (positions.Count == 0)
+ return null;
+
+ string str = "";
+ foreach (var p in positions.Values)
+ {
+ str += $"{p.Symbol},{p.Side.ToString()},{p.HistoryPosition}+{p.TodayPosition}={p.Position}\n";
+
+ }
+ str += $"\n合约,多空,昨+今=总";
+ str += $"\n>>{DateTime.Now.ToLongTimeString()}<<";
+ plog.Info(str);
+
+ return str;
+ }
+ }
+}
diff --git a/QuantBox.API.Provider/Single/SingleProvider.DataProvider.cs b/QuantBox.APIProvider/Single/SingleProvider.DataProvider.cs
similarity index 96%
rename from QuantBox.API.Provider/Single/SingleProvider.DataProvider.cs
rename to QuantBox.APIProvider/Single/SingleProvider.DataProvider.cs
index 45a2957..caa93bf 100644
--- a/QuantBox.API.Provider/Single/SingleProvider.DataProvider.cs
+++ b/QuantBox.APIProvider/Single/SingleProvider.DataProvider.cs
@@ -40,11 +40,13 @@ public override void Subscribe(Instrument instrument)
string apiSymbol;
string apiExchange;
double apiTickSize;
+ string apiProductID;
GetApi_Symbol_Exchange_TickSize(instrument, this.id,
out altSymbol, out altExchange,
out apiSymbol, out apiExchange,
- out apiTickSize);
+ out apiTickSize,
+ out apiProductID);
// 如果是对CTP接口,使用UFX的参数进行订阅是否有问题?IF1802.7,目前猜没有问题
string Symbol_Dot_Exchange = string.Format("{0}.{1}", apiSymbol, apiExchange);
@@ -88,11 +90,13 @@ public override void Unsubscribe(Instrument instrument)
string apiSymbol;
string apiExchange;
double apiTickSize;
+ string apiProductID;
GetApi_Symbol_Exchange_TickSize(instrument, this.id,
out altSymbol, out altExchange,
out apiSymbol, out apiExchange,
- out apiTickSize);
+ out apiTickSize,
+ out apiProductID);
string Symbol_Dot_Exchange = string.Format("{0}.{1}", apiSymbol, apiExchange);
string Symbol_Dot = string.Format("{0}.", apiSymbol);
diff --git a/QuantBox.API.Provider/Single/SingleProvider.ExecutionProvider.cs b/QuantBox.APIProvider/Single/SingleProvider.ExecutionProvider.cs
similarity index 100%
rename from QuantBox.API.Provider/Single/SingleProvider.ExecutionProvider.cs
rename to QuantBox.APIProvider/Single/SingleProvider.ExecutionProvider.cs
diff --git a/QuantBox.API.Provider/Single/SingleProvider.HistoricalDataProvider.cs b/QuantBox.APIProvider/Single/SingleProvider.HistoricalDataProvider.cs
similarity index 96%
rename from QuantBox.API.Provider/Single/SingleProvider.HistoricalDataProvider.cs
rename to QuantBox.APIProvider/Single/SingleProvider.HistoricalDataProvider.cs
index f874b84..031ce5c 100644
--- a/QuantBox.API.Provider/Single/SingleProvider.HistoricalDataProvider.cs
+++ b/QuantBox.APIProvider/Single/SingleProvider.HistoricalDataProvider.cs
@@ -19,11 +19,13 @@ private HistoricalDataRequestField ToStruct(HistoricalDataRequest request)
string apiSymbol;
string apiExchange;
double apiTickSize;
+ string apiProductID;
GetApi_Symbol_Exchange_TickSize(request.Instrument, this.id,
out altSymbol, out altExchange,
out apiSymbol, out apiExchange,
- out apiTickSize);
+ out apiTickSize,
+ out apiProductID);
HistoricalDataRequestField field = new HistoricalDataRequestField();
field.Symbol = request.Instrument.Symbol;
diff --git a/QuantBox.API.Provider/Single/SingleProvider.InstrumentProvider.cs b/QuantBox.APIProvider/Single/SingleProvider.InstrumentProvider.cs
similarity index 100%
rename from QuantBox.API.Provider/Single/SingleProvider.InstrumentProvider.cs
rename to QuantBox.APIProvider/Single/SingleProvider.InstrumentProvider.cs
diff --git a/QuantBox.API.Provider/Single/SingleProvider.Other.cs b/QuantBox.APIProvider/Single/SingleProvider.Other.cs
similarity index 84%
rename from QuantBox.API.Provider/Single/SingleProvider.Other.cs
rename to QuantBox.APIProvider/Single/SingleProvider.Other.cs
index 50e387d..137b9fc 100644
--- a/QuantBox.API.Provider/Single/SingleProvider.Other.cs
+++ b/QuantBox.APIProvider/Single/SingleProvider.Other.cs
@@ -11,10 +11,11 @@ namespace QuantBox.APIProvider.Single
public partial class SingleProvider
{
// 得到API中的合约名与交易所
- private void GetApi_Symbol_Exchange_TickSize(Instrument instrument, byte id,
+ public void GetApi_Symbol_Exchange_TickSize(Instrument instrument, byte id,
out string altSymbol, out string altExchange,
out string apiSymbol, out string apiExchange,
- out double apiTickSize)
+ out double apiTickSize,
+ out string apiProductID)
{
// 取合约别名
altSymbol = instrument.GetSymbol(id);
@@ -24,6 +25,7 @@ private void GetApi_Symbol_Exchange_TickSize(Instrument instrument, byte id,
// 取合约在API中的名字
apiSymbol = altSymbol;
apiExchange = altExchange;
+ apiProductID = "";
// 对于UFX,没有实现查询合约的功能,所以这里其实使用的是AltID中的信息
// 屏蔽这个功能,订阅的合约就根据设置来了
@@ -35,6 +37,7 @@ private void GetApi_Symbol_Exchange_TickSize(Instrument instrument, byte id,
apiSymbol = _Instrument.InstrumentID;
apiExchange = _Instrument.ExchangeID;
apiTickSize = _Instrument.PriceTick;
+ apiProductID = _Instrument.ProductID;
}
}
}
diff --git a/QuantBox.API.Provider/Single/SingleProvider.Provider.cs b/QuantBox.APIProvider/Single/SingleProvider.Provider.cs
similarity index 97%
rename from QuantBox.API.Provider/Single/SingleProvider.Provider.cs
rename to QuantBox.APIProvider/Single/SingleProvider.Provider.cs
index b727da0..9ef770a 100644
--- a/QuantBox.API.Provider/Single/SingleProvider.Provider.cs
+++ b/QuantBox.APIProvider/Single/SingleProvider.Provider.cs
@@ -16,6 +16,8 @@ namespace QuantBox.APIProvider.Single
public partial class SingleProvider:Provider
{
private Logger xlog;
+ private Logger alog;
+ private Logger plog;
private Logger barLog;
private Logger tickLog;
@@ -56,6 +58,8 @@ public void Init(byte id, string name)
// 只是简单设置,等登录时将把账号设置上,X日志由于一些信息没法
xlog = LogManager.GetLogger(Name + ".X");
+ alog = LogManager.GetLogger(Name + ".A");
+ plog = LogManager.GetLogger(Name + ".P");
barLog = LogManager.GetLogger("Bar");
tickLog = LogManager.GetLogger("Tick");
@@ -64,7 +68,7 @@ public void Init(byte id, string name)
EmitBidAsk = true;
EmitLevel2Snapshot = false;
//UpdateInstrument = true;
- EmitHistoricalData = true;
+ EnablEmitHistoricalData = true;
FilterDateTime = true;
EnableEmitData = true;
HasPriceLimit = true;
@@ -87,7 +91,7 @@ public void Init(byte id, string name)
historicalDataIds = new Dictionary();
// ConfigPath在做Setting时已经做了
- //Load();
+ Load();
}
void SessionTimeList_ListChanged(object sender, ListChangedEventArgs e)
diff --git a/QuantBox.API.Provider/Single/SingleProvider.Settings.cs b/QuantBox.APIProvider/Single/SingleProvider.Settings.cs
similarity index 93%
rename from QuantBox.API.Provider/Single/SingleProvider.Settings.cs
rename to QuantBox.APIProvider/Single/SingleProvider.Settings.cs
index 302eee1..54e51f3 100644
--- a/QuantBox.API.Provider/Single/SingleProvider.Settings.cs
+++ b/QuantBox.APIProvider/Single/SingleProvider.Settings.cs
@@ -1,17 +1,14 @@
-using NLog;
-using QuantBox.APIProvider.UI;
-using XAPI;
+using XAPI;
using SmartQuant;
-using System;
-using System.Collections.Generic;
using System.ComponentModel;
-using System.Drawing.Design;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+
+using QuantBox.APIProvider.UI;
namespace QuantBox.APIProvider.Single
{
+#if NET48
+ using System.Drawing.Design;
+#endif
public partial class SingleProvider : Provider
{
private const string CATEGORY_SETTINGS = "Settings";
@@ -130,9 +127,12 @@ public int QueryPositionInterval
#region 通用
+
+#if NET48
+ [Editor(typeof(System.Windows.Forms.Design.FolderNameEditor), typeof(UITypeEditor))]
+#endif
[Category(CATEGORY_COMMON)]
[Description("配置文件路径")]
- [Editor(typeof(System.Windows.Forms.Design.FolderNameEditor), typeof(UITypeEditor))]
public string ConfigPath { get; set; }
[Category(CATEGORY_COMMON)]
@@ -140,12 +140,18 @@ public int QueryPositionInterval
public BindingList SessionTimeList { get; set; }
#endregion
- [Category(CATEGORY_SETTINGS), Editor(typeof(ApiManagerTypeEditor), typeof(UITypeEditor)),
- Description("综合设置")]
+#if NET48
+ [Editor(typeof(ApiManagerTypeEditor), typeof(UITypeEditor))]
+#endif
+ [Category(CATEGORY_SETTINGS)]
+ [Description("综合设置")]
public string AllConfig { get; set; }
- [Category(CATEGORY_SETTINGS), Editor(typeof(ApiControlTypeEditor), typeof(UITypeEditor)),
- Description("综合控制")]
+#if NET48
+ [Editor(typeof(ApiManagerTypeEditor), typeof(UITypeEditor))]
+#endif
+ [Category(CATEGORY_SETTINGS)]
+ [Description("综合控制")]
public string AllControl { get; set; }
[Browsable(false)]
@@ -173,7 +179,7 @@ public int QueryPositionInterval
[Category(CATEGORY_HISTORICAL_DATA)]
[Description("【历史】是否触发EmitHistoricalData事件")]
[DisplayName("EmitHistoricalData")]
- public bool EmitHistoricalData { get; set; }
+ public bool EnablEmitHistoricalData { get; set; }
[Category(CATEGORY_HISTORICAL_DATA)]
[Description("【历史】是否过滤数据日期和时间")]
diff --git a/QuantBox.APIProvider/Single/UserItem.cs b/QuantBox.APIProvider/Single/UserItem.cs
new file mode 100644
index 0000000..5b97973
--- /dev/null
+++ b/QuantBox.APIProvider/Single/UserItem.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XAPI;
+
+#if NET48
+using System.Drawing.Design;
+#endif
+
+namespace QuantBox.APIProvider.Single
+{
+ [DefaultProperty("Label")]
+ public class UserItem:ICloneable
+ {
+ [Category("标签")]
+ public string Label
+ {
+ get;
+ set;
+ }
+#if NET48
+ [Editor(typeof(System.Windows.Forms.Design.FileNameEditor), typeof(UITypeEditor))]
+#endif
+ public string Path { get; set; }
+
+ public override string ToString()
+ {
+ return string.Format("Label={0}", this.Label);
+ }
+
+ public object Clone()
+ {
+ return this.MemberwiseClone();
+ }
+ }
+}
diff --git a/QuantBox.API.Provider/UI/ApiControlForm.Designer.cs b/QuantBox.APIProvider/UI/ApiControlForm.Designer.cs
similarity index 99%
rename from QuantBox.API.Provider/UI/ApiControlForm.Designer.cs
rename to QuantBox.APIProvider/UI/ApiControlForm.Designer.cs
index 881b246..3a43a92 100644
--- a/QuantBox.API.Provider/UI/ApiControlForm.Designer.cs
+++ b/QuantBox.APIProvider/UI/ApiControlForm.Designer.cs
@@ -1,5 +1,6 @@
namespace QuantBox.APIProvider.UI
{
+#if NET48
partial class ApiControlForm
{
///
@@ -20,7 +21,7 @@ protected override void Dispose(bool disposing)
base.Dispose(disposing);
}
- #region Windows Form Designer generated code
+ #region Windows Form Designer generated code
///
/// Required method for Designer support - do not modify
@@ -188,7 +189,7 @@ private void InitializeComponent()
}
- #endregion
+ #endregion
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.Label label1;
@@ -204,4 +205,5 @@ private void InitializeComponent()
private System.Windows.Forms.TextBox textBox_PortfolioID2;
private System.Windows.Forms.TextBox textBox_PortfolioID1;
}
+#endif
}
\ No newline at end of file
diff --git a/QuantBox.API.Provider/UI/ApiControlForm.cs b/QuantBox.APIProvider/UI/ApiControlForm.cs
similarity index 92%
rename from QuantBox.API.Provider/UI/ApiControlForm.cs
rename to QuantBox.APIProvider/UI/ApiControlForm.cs
index 4b09b61..87463c6 100644
--- a/QuantBox.API.Provider/UI/ApiControlForm.cs
+++ b/QuantBox.APIProvider/UI/ApiControlForm.cs
@@ -1,17 +1,12 @@
using QuantBox.APIProvider.Single;
using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Data;
-using System.Drawing;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Forms;
+
using XAPI;
namespace QuantBox.APIProvider.UI
{
+#if NET48
+ using System.Windows.Forms;
public partial class ApiControlForm : Form
{
public ApiControlForm()
@@ -74,4 +69,5 @@ private void ApiControlForm_Load(object sender, EventArgs e)
comboBox_BusinessType.DataSource = Enum.GetValues(typeof(BusinessType));
}
}
+#endif
}
diff --git a/QuantBox.API.Provider/UI/ApiControlForm.resx b/QuantBox.APIProvider/UI/ApiControlForm.resx
similarity index 100%
rename from QuantBox.API.Provider/UI/ApiControlForm.resx
rename to QuantBox.APIProvider/UI/ApiControlForm.resx
diff --git a/QuantBox.API.Provider/UI/ApiControlTypeEditor.cs b/QuantBox.APIProvider/UI/ApiControlTypeEditor.cs
similarity index 85%
rename from QuantBox.API.Provider/UI/ApiControlTypeEditor.cs
rename to QuantBox.APIProvider/UI/ApiControlTypeEditor.cs
index 6b33d85..365ff2d 100644
--- a/QuantBox.API.Provider/UI/ApiControlTypeEditor.cs
+++ b/QuantBox.APIProvider/UI/ApiControlTypeEditor.cs
@@ -1,15 +1,14 @@
-using QuantBox.APIProvider.Single;
-using System;
-using System.Collections.Generic;
+using System;
using System.ComponentModel;
-using System.Drawing.Design;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Forms.Design;
+
+using QuantBox.APIProvider.Single;
namespace QuantBox.APIProvider.UI
{
+#if NET48
+ using System.Drawing.Design;
+ using System.Windows.Forms.Design;
+
class ApiControlTypeEditor : UITypeEditor
{
// Methods
@@ -41,4 +40,5 @@ public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext contex
return base.GetEditStyle(context);
}
}
+#endif
}
diff --git a/QuantBox.API.Provider/UI/ApiManagerForm.Designer.cs b/QuantBox.APIProvider/UI/ApiManagerForm.Designer.cs
similarity index 99%
rename from QuantBox.API.Provider/UI/ApiManagerForm.Designer.cs
rename to QuantBox.APIProvider/UI/ApiManagerForm.Designer.cs
index 7c5d0f2..cb5030b 100644
--- a/QuantBox.API.Provider/UI/ApiManagerForm.Designer.cs
+++ b/QuantBox.APIProvider/UI/ApiManagerForm.Designer.cs
@@ -1,5 +1,6 @@
namespace QuantBox.APIProvider.UI
{
+#if NET48
partial class ApiManagerForm
{
///
@@ -20,7 +21,7 @@ protected override void Dispose(bool disposing)
base.Dispose(disposing);
}
- #region Windows Form Designer generated code
+#region Windows Form Designer generated code
///
/// Required method for Designer support - do not modify
@@ -288,7 +289,7 @@ private void InitializeComponent()
}
- #endregion
+#endregion
private System.Windows.Forms.ListBox listBox_UserList;
private System.Windows.Forms.PropertyGrid propertyGrid;
@@ -310,4 +311,5 @@ private void InitializeComponent()
private System.Windows.Forms.BindingSource serverItemBindingSource;
private System.Windows.Forms.BindingSource apiItemBindingSource;
}
+#endif
}
\ No newline at end of file
diff --git a/QuantBox.API.Provider/UI/ApiManagerForm.cs b/QuantBox.APIProvider/UI/ApiManagerForm.cs
similarity index 99%
rename from QuantBox.API.Provider/UI/ApiManagerForm.cs
rename to QuantBox.APIProvider/UI/ApiManagerForm.cs
index 1bee632..e493594 100644
--- a/QuantBox.API.Provider/UI/ApiManagerForm.cs
+++ b/QuantBox.APIProvider/UI/ApiManagerForm.cs
@@ -7,10 +7,12 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using System.Windows.Forms;
+
namespace QuantBox.APIProvider.UI
{
+#if NET48
+ using System.Windows.Forms;
public partial class ApiManagerForm : Form
{
public ApiManagerForm()
@@ -169,4 +171,5 @@ private void ApiManagerForm_FormClosed(object sender, FormClosedEventArgs e)
provider.Save();
}
}
+#endif
}
diff --git a/QuantBox.API.Provider/UI/ApiManagerForm.resx b/QuantBox.APIProvider/UI/ApiManagerForm.resx
similarity index 100%
rename from QuantBox.API.Provider/UI/ApiManagerForm.resx
rename to QuantBox.APIProvider/UI/ApiManagerForm.resx
diff --git a/QuantBox.API.Provider/UI/ApiManagerTypeEditor.cs b/QuantBox.APIProvider/UI/ApiManagerTypeEditor.cs
similarity index 85%
rename from QuantBox.API.Provider/UI/ApiManagerTypeEditor.cs
rename to QuantBox.APIProvider/UI/ApiManagerTypeEditor.cs
index f6273e4..fb58f6c 100644
--- a/QuantBox.API.Provider/UI/ApiManagerTypeEditor.cs
+++ b/QuantBox.APIProvider/UI/ApiManagerTypeEditor.cs
@@ -1,15 +1,14 @@
-using QuantBox.APIProvider.Single;
-using System;
-using System.Collections.Generic;
+using System;
using System.ComponentModel;
-using System.Drawing.Design;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Forms.Design;
+
+using QuantBox.APIProvider.Single;
namespace QuantBox.APIProvider.UI
{
+#if NET48
+ using System.Drawing.Design;
+ using System.Windows.Forms.Design;
+
class ApiManagerTypeEditor : UITypeEditor
{
// Methods
@@ -40,4 +39,5 @@ public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext contex
return base.GetEditStyle(context);
}
}
+#endif
}
diff --git a/QuantBox.API.Provider/UI/ApiTypeSelectorEditor.cs b/QuantBox.APIProvider/UI/ApiTypeSelectorEditor.cs
similarity index 91%
rename from QuantBox.API.Provider/UI/ApiTypeSelectorEditor.cs
rename to QuantBox.APIProvider/UI/ApiTypeSelectorEditor.cs
index b42b388..e639961 100644
--- a/QuantBox.API.Provider/UI/ApiTypeSelectorEditor.cs
+++ b/QuantBox.APIProvider/UI/ApiTypeSelectorEditor.cs
@@ -1,17 +1,16 @@
using QuantBox.APIProvider.Single;
using System;
-using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Forms;
+
using XAPI;
namespace QuantBox.APIProvider.UI
{
+#if NET48
+ using System.Windows.Forms;
+
class ApiTypeSelectorEditor : ObjectSelectorEditor
{
private ObjectSelectorEditor.Selector selector;
@@ -44,7 +43,7 @@ protected override void FillTreeWithData(ObjectSelectorEditor.Selector selector,
{
if (category != ApiType.None)
{
- if((instance.Type & category) == category)
+ if ((instance.Type & category) == category)
{
selector.AddNode(category.ToString(), (int)category, null).Checked = (instance.UseType & category) == category;
}
@@ -63,5 +62,5 @@ private void method_0(object sender, TreeViewCancelEventArgs e)
e.Cancel = true;
}
}
-
+#endif
}
diff --git a/QuantBox.API.Provider/UI/ComboBoxItemTypeConvert.cs b/QuantBox.APIProvider/UI/ComboBoxItemTypeConvert.cs
similarity index 100%
rename from QuantBox.API.Provider/UI/ComboBoxItemTypeConvert.cs
rename to QuantBox.APIProvider/UI/ComboBoxItemTypeConvert.cs
diff --git a/QuantBox.API.Provider/UI/JTypeDescriptor.cs b/QuantBox.APIProvider/UI/JTypeDescriptor.cs
similarity index 100%
rename from QuantBox.API.Provider/UI/JTypeDescriptor.cs
rename to QuantBox.APIProvider/UI/JTypeDescriptor.cs
diff --git a/QuantBox.API.Provider/UI/ServerItemConverter.cs b/QuantBox.APIProvider/UI/ServerItemConverter.cs
similarity index 100%
rename from QuantBox.API.Provider/UI/ServerItemConverter.cs
rename to QuantBox.APIProvider/UI/ServerItemConverter.cs
diff --git a/QuantBox.API.Provider/UI/UserItemConverter.cs b/QuantBox.APIProvider/UI/UserItemConverter.cs
similarity index 81%
rename from QuantBox.API.Provider/UI/UserItemConverter.cs
rename to QuantBox.APIProvider/UI/UserItemConverter.cs
index 2434f31..b45af1a 100644
--- a/QuantBox.API.Provider/UI/UserItemConverter.cs
+++ b/QuantBox.APIProvider/UI/UserItemConverter.cs
@@ -1,11 +1,7 @@
using QuantBox.APIProvider.Single;
-using System;
-using System.Collections;
-using System.Collections.Generic;
+
using System.ComponentModel;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+
namespace QuantBox.APIProvider.UI
{
diff --git a/QuantBox.APIProvider_Windows.sln b/QuantBox.APIProvider_Windows.sln
deleted file mode 100644
index 6e9aac4..0000000
--- a/QuantBox.APIProvider_Windows.sln
+++ /dev/null
@@ -1,31 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2013
-VisualStudioVersion = 12.0.21005.1
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuantBox.APIProvider", "QuantBox.API.Provider\QuantBox.APIProvider.csproj", "{E86E1057-B344-4805-A836-43606154677E}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuantBox.Extensions", "QuantBox.Extensions\QuantBox.Extensions.csproj", "{D8D82E27-47F5-4579-92AD-416DA86AD601}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {E86E1057-B344-4805-A836-43606154677E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {E86E1057-B344-4805-A836-43606154677E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {E86E1057-B344-4805-A836-43606154677E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {E86E1057-B344-4805-A836-43606154677E}.Release|Any CPU.Build.0 = Release|Any CPU
- {D8D82E27-47F5-4579-92AD-416DA86AD601}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D8D82E27-47F5-4579-92AD-416DA86AD601}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D8D82E27-47F5-4579-92AD-416DA86AD601}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D8D82E27-47F5-4579-92AD-416DA86AD601}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(MonoDevelopProperties) = preSolution
- StartupItem = SMACrossover\Realtime\Realtime.csproj
- EndGlobalSection
-EndGlobal
diff --git a/QuantBox.Extensions/QuantBox.Extensions.csproj b/QuantBox.Extensions/QuantBox.Extensions.csproj
index 7c011a7..1283ef8 100644
--- a/QuantBox.Extensions/QuantBox.Extensions.csproj
+++ b/QuantBox.Extensions/QuantBox.Extensions.csproj
@@ -1,79 +1,17 @@
-
-
-
+
+
- Debug
- AnyCPU
- {D8D82E27-47F5-4579-92AD-416DA86AD601}
- Library
- Properties
- QuantBox.Extensions
- QuantBox.Extensions
- v4.6.1
- 512
-
-
-
- true
- full
- false
- C:\Program Files\SmartQuant Ltd\OpenQuant 2014\
- DEBUG;TRACE
- prompt
- 4
-
-
- pdbonly
- true
- C:\Program Files\SmartQuant Ltd\OpenQuant 2014\
- TRACE
- prompt
- 4
-
-
-
+ netstandard2.0
+ false
+
- False
C:\Program Files\SmartQuant Ltd\OpenQuant 2014\SmartQuant.dll
-
-
-
-
-
- False
- C:\Program Files\SmartQuant Ltd\OpenQuant 2014\XAPI_CSharp.exe
+ C:\Program Files\SmartQuant Ltd\OpenQuant 2014\XAPI_CSharp.dll
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
diff --git a/QuantBox.Extensions/QuantBox.Extensions_Linux.csproj b/QuantBox.Extensions/QuantBox.Extensions_Linux.csproj
deleted file mode 100644
index ac1f5d4..0000000
--- a/QuantBox.Extensions/QuantBox.Extensions_Linux.csproj
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
-
- Debug
- AnyCPU
- {D8D82E27-47F5-4579-92AD-416DA86AD601}
- Library
- Properties
- QuantBox.Extensions
- QuantBox.Extensions
- v4.5.1
- 512
-
-
-
- true
- full
- false
- C:\Program Files\SmartQuant Ltd\OpenQuant 2014\
- DEBUG;TRACE
- prompt
- 4
-
-
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
-
-
-
-
-
-
-
-
- ..\..\bin\SmartQuant.dll
-
-
- ..\..\bin\QuantBox.XAPI.dll
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/README.md b/README.md
index fe735b2..0291a5a 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,41 @@
-#QuantBox.APIProvider
+# QuantBox.APIProvider
+
+OpenQuant2014的行情交易插件,使用XAPI统一接口
+
+## 命令启停OQ功能
+
+1. 启动OQ,并打开指定策略,并运行
+通过**命令行**传入参数
+- --file: 策略绝对路径。如果空格必须用引号
+- --run: 运行策略。否则只打开策略
+- --id: 识别码。用于监控剪贴板时过滤命令
+
+```
+cd "C:\Program Files\SmartQuant Ltd\OpenQuant 2014"
+C:
+start OpenQuant.exe --file="D:\Users\Kan\Documents\OpenQuant 2014\Solutions\SMACrossover\SMACrossover.sln" --id=100 --run
+```
+
+2. 停止策略,并退出程序
+通过**剪贴板**传入参数。
+- --id: 识别码。用于监控剪贴板时过滤命令
+- --stop: 停止策略。必须先停止才能退出
+- --exit: 退出程序。
+```
+echo --id=100 --stop --exit | clip
+```
+只要向剪贴板复制`--id=100 --stop --exit`即可,这个复制可以手工实现,也可以灵活使用管道符|将echo的回显重定向到剪贴板clip
+
+## 开盘前自动连接并通知
+
+此插件在登录成功时会自动查询资金,可以将其转发到钉钉,这样用户就可以知道连接已经成功。
+1. 设置SessionTimeList,需要每个星期都有,包括周日。
+2. 设置NLog.config,如当前插件名为A99CTP,那么NLog中资金的logger是A99CTP.A
+3. 将A99CTP.A转发到WebService,如钉钉
+
+
+## 定时通知资金与持仓功能
+
+通过设置参数QueryAccountInterval(资金查询间隔,秒)和QueryPositionInterval(持仓查询间隔,秒),可以自动查询,并输出日志。
+1. 资金logger:A99CTP.A
+2. 持仓logger:A99CTP.P
\ No newline at end of file
diff --git a/config/Clock.reg b/config/Clock.reg
new file mode 100644
index 0000000..081b4af
Binary files /dev/null and b/config/Clock.reg differ
diff --git a/config/NLog.config b/config/NLog.config
new file mode 100644
index 0000000..4fb04bd
--- /dev/null
+++ b/config/NLog.config
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/NTP.reg b/config/NTP.reg
new file mode 100644
index 0000000..c90b456
Binary files /dev/null and b/config/NTP.reg differ
diff --git a/config/backup_proj.bat b/config/backup_proj.bat
new file mode 100644
index 0000000..fd652b4
--- /dev/null
+++ b/config/backup_proj.bat
@@ -0,0 +1,6 @@
+cd D:\
+D:
+set DATA_YMD=%date:~0,4%%date:~5,2%%date:~8,2%
+xcopy S1 S1_ά\%DATA_YMD%\
+xcopy S2 S2_ά\%DATA_YMD%\
+pause
\ No newline at end of file
diff --git a/config/run_proj.bat b/config/run_proj.bat
new file mode 100644
index 0000000..fdc7374
--- /dev/null
+++ b/config/run_proj.bat
@@ -0,0 +1,9 @@
+cd "C:\Program Files\SmartQuant Ltd\OpenQuant 2014"
+C:
+
+start OpenQuant.exe --file="D:\GitHub\S1\S1.sln" --id=100 --run
+ping 127.0.0.1 -n 20
+start OpenQuant.exe --file="D:\GitHub\S2\S2.sln" --id=200 --run
+ping 127.0.0.1 -n 20
+
+pause
diff --git a/config/stop_proj.bat b/config/stop_proj.bat
new file mode 100644
index 0000000..a952368
--- /dev/null
+++ b/config/stop_proj.bat
@@ -0,0 +1,6 @@
+echo --id=100 --stop --exit | clip
+ping 127.0.0.1 -n 10
+echo --id=200 --stop --exit | clip
+ping 127.0.0.1 -n 10
+
+pause
\ No newline at end of file
diff --git a/python/BatchDelayQueue.py b/python/BatchDelayQueue.py
new file mode 100644
index 0000000..4244561
--- /dev/null
+++ b/python/BatchDelayQueue.py
@@ -0,0 +1,57 @@
+"""
+延迟队列
+"""
+import threading
+import time
+
+
+class BatchQueue:
+ def __init__(self):
+ self.buf = []
+
+ def add_one(self, obj):
+ self.buf.append(obj)
+
+ def get_all(self):
+ _buf = self.buf.copy()
+ self.buf = []
+ return _buf
+
+
+class DelayQueue:
+
+ def __init__(self, interval, process_func):
+ self.queue = BatchQueue()
+ self.interval = interval
+ self.process_func = process_func
+ # 启动定时器
+ threading.Timer(self.interval, self.process).start()
+
+ def add(self, obj):
+ self.queue.add_one(obj)
+
+ def process(self):
+ buf = self.queue.get_all()
+ if len(buf) > 0:
+ self.process_func(buf)
+ # 定时器需要再激活才可使用
+ threading.Timer(self.interval, self.process).start()
+
+
+if __name__ == '__main__':
+
+ def process(buf):
+ print(buf)
+
+
+ def thread_fun(dq):
+ for i in range(1000):
+ dq.add(i)
+ time.sleep(0.01)
+
+
+ dq = DelayQueue(5, process)
+ threading.Thread(target=thread_fun, args=(dq,)).start()
+ threading.Thread(target=thread_fun, args=(dq,)).start()
+ threading.Thread(target=thread_fun, args=(dq,)).start()
+ input()
diff --git a/python/DtalkRobot.py b/python/DtalkRobot.py
new file mode 100644
index 0000000..e4f8c69
--- /dev/null
+++ b/python/DtalkRobot.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+钉钉群自定义机器人
+author:疯狂的技术宅
+github:https://github.com/magician000
+
+学习python时做的练习,纯粹为了娱乐
+如果存在bug请自行修改,不提供任何支持
+
+官方文档
+https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.z5MWoh&treeId=257&articleId=105735&docType=1
+
+这个接口的消息格式命名风格不统一,坑爹呢?
+所以不要迷信大公司就怎样规范。
+"""
+
+import urllib.parse
+import urllib.request
+import json
+import time
+
+# 代理,一般不使用
+if False:
+ proxy_support = urllib.request.ProxyHandler({'http': '192.168.1.60:808', 'https': '192.168.1.60:808'})
+ # proxy_support = urllib.request.ProxyHandler({'sock5': '192.168.1.60:1080'})
+ opener = urllib.request.build_opener(proxy_support)
+ urllib.request.install_opener(opener)
+
+
+# 自定义机器人的封装类
+class DtalkRobot(object):
+ def __init__(self, webhook):
+ super(DtalkRobot, self).__init__()
+ self.webhook = webhook
+
+ # text类型
+ def sendText_webhook(self, webhook, msg, isAtAll=False, atMobiles=[]):
+ data = {"msgtype": "text", "text": {"content": msg}, "at": {"atMobiles": atMobiles, "isAtAll": isAtAll}}
+ return self.post_webhook(webhook, data)
+
+ # markdown类型
+ def sendMarkdown_webhook(self, webhook, title, text):
+ data = {"msgtype": "markdown", "markdown": {"title": title, "text": text}}
+ return self.post_webhook(webhook, data)
+
+ # link类型
+ def sendLink(self, title, text, messageUrl, picUrl=""):
+ data = {"msgtype": "link",
+ "link": {"text": text, "title": title, "picUrl": picUrl, "messageUrl": messageUrl}}
+ return self.post(data)
+
+ # ActionCard类型
+ def sendActionCard(self, actionCard):
+ data = actionCard.getData()
+ return self.post(data)
+
+ # FeedCard类型
+ def sendFeedCard(self, links):
+ data = {"feedCard": {"links": links}, "msgtype": "feedCard"}
+ return self.post(data)
+
+ def post(self, data):
+ post_data = json.JSONEncoder().encode(data).encode(encoding='UTF8')
+
+ req = urllib.request.Request(self.webhook, post_data)
+ req.add_header('Content-Type', 'application/json')
+ content = urllib.request.urlopen(req).read().decode('UTF-8')
+ return content
+
+ def post_webhook(self, webhook, data):
+ post_data = json.JSONEncoder().encode(data).encode(encoding='UTF8')
+
+ req = urllib.request.Request(webhook, post_data)
+ req.add_header('Content-Type', 'application/json')
+ content = urllib.request.urlopen(req).read().decode('UTF-8')
+ # print(content)
+ return content
+
+
+# ActionCard类型消息结构
+class ActionCard(object):
+ """docstring for ActionCard"""
+ title = ""
+ text = ""
+ singleTitle = ""
+ singleURL = ""
+ btnOrientation = 0
+ hideAvatar = 0
+ btns = []
+
+ def __init__(self, arg=""):
+ super(ActionCard, self).__init__()
+ self.arg = arg
+
+ def putBtn(self, title, actionURL):
+ self.btns.append({"title": title, "actionURL": actionURL})
+
+ def getData(self):
+ data = {"actionCard": {"title": self.title, "text": self.text, "hideAvatar": self.hideAvatar,
+ "btnOrientation": self.btnOrientation, "singleTitle": self.singleTitle,
+ "singleURL": self.singleURL, "btns": self.btns}, "msgtype": "actionCard"}
+ return data
+
+
+# FeedCard类型消息格式
+class FeedLink(object):
+ """docstring for FeedLink"""
+ title = ""
+ picUrl = ""
+ messageUrl = ""
+
+ def __init__(self, arg=""):
+ super(FeedLink, self).__init__()
+ self.arg = arg
+
+ def getData(self):
+ data = {"title": self.title, "picURL": self.picUrl, "messageURL": self.messageUrl}
+ return data
+
+
+if __name__ == "__main__":
+ webhook = "https://oapi.dingtalk.com/robot/send?access_token=72f6252257f43f402f743b7d698fc21d21939717783a20689e1c1292c20d1903"
+ robot = DtalkRobot(webhook)
+
+ # print(robot.sendText("xxxx:[" + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())) + "]", False,
+ # ["13912345678 "]))
+
+ print(robot.sendMarkdown('报单回报', """# LAST_HEDGE (OI805)
+1. 21:00:00.224/6298/0|-2/
+1. 21:00:01.412/6296/0
+## LAST_HEDGE (TA805)
+1. 21:00:00.224/5692/0|-6/
+1. 21:00:01.427/5690/0
+### LAST_HEDGE (TA805)
+- 21:00:00.224/5692/0|-6/
+- 21:00:01.427/5690/0
+#### LAST_HEDGE (TA805)
+- 21:00:00.224/5692/0|-6/
+- 21:00:01.427/5690/0
+##### LAST_HEDGE (TA805)
+- 21:00:00.224/5692/0|-6/
+- 21:00:01.427/5690/0
+###### LAST_HEDGE (i1805)
+- 21:00:00.427/514.5/0|1/
+- 21:00:01.412/514/0"""))
diff --git a/python/dtlogger.py b/python/dtlogger.py
new file mode 100644
index 0000000..136b0ee
--- /dev/null
+++ b/python/dtlogger.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+pip install spyne
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+protected NLog.Logger log_wx = NLog.LogManager.GetLogger("wx");
+
+log_wx.Warn("策略启动。");
+
+"""
+from spyne import Application, rpc, ServiceBase, String
+from spyne.protocol.http import HttpRpc
+from spyne.protocol.json import JsonDocument
+from spyne.server.wsgi import WsgiApplication
+
+from DtalkRobot import *
+from BatchDelayQueue import DelayQueue
+
+# 99simnow
+webhook_a99ctp = "https://oapi.dingtalk.com/robot/send?access_token=11111"
+# 98实
+webhook_a98ctp = "https://oapi.dingtalk.com/robot/send?access_token=11111"
+# S01策略
+webhook_s01 = "https://oapi.dingtalk.com/robot/send?access_token=11111"
+
+# 不明的消息都发到模拟平台上
+webhook = webhook_a99ctp
+
+ip = '0.0.0.0'
+port = 8000
+
+
+def send_msgs(logger, level, title, message):
+ # 利用logger,可以将消息发向不同的机器人
+ if logger == 'ALL':
+ _webhook = webhook
+ elif logger == 'A98CTP.A':
+ _webhook = webhook_a98ctp
+ elif logger == 'A98CTP.P':
+ _webhook = webhook_a98ctp
+ elif logger == 'S01':
+ _webhook = webhook_s01
+ else:
+ _webhook = webhook
+
+ if title is None or len(title) == 0:
+ robot.sendText_webhook(_webhook, message)
+ else:
+ msg = f"##### {title}\n{message}"
+ robot.sendMarkdown_webhook(_webhook, title, msg)
+
+
+def process_log_msg(tuples):
+ msgs = {}
+ for tp in tuples:
+ logger, level, title, message = tp
+ key = (logger, level, title)
+ lst = msgs.get(key, [])
+ lst.append(message)
+ msgs[key] = lst
+ print("定时处理", len(msgs))
+ for k, v in msgs.items():
+ # 这里不能太长,否则会被截断
+ send_msgs(k[0], k[1], k[2], '\n'.join(v))
+
+
+class LogService(ServiceBase):
+ @rpc(String, String, String, String, _returns=String)
+ def logme(ctx, logger, level, title, message):
+ delayQueue.add((logger, level, title, message))
+ return 'OK'
+
+
+delayQueue = DelayQueue(10, process_log_msg)
+
+application = Application([LogService],
+ tns='kan.logger.wechat',
+ in_protocol=HttpRpc(validator='soft'),
+ out_protocol=JsonDocument()
+ )
+
+if __name__ == '__main__':
+ # You can use any Wsgi server. Here, we chose
+ # Python's built-in wsgi server but you're not
+ # supposed to use it in production.
+ from wsgiref.simple_server import make_server
+
+ wsgi_app = WsgiApplication(application)
+ server = make_server(ip, port, wsgi_app)
+
+ robot = DtalkRobot(webhook)
+ robot.sendText_webhook(webhook, "启动钉钉日志服务成功")
+
+ # 不退出
+ server.serve_forever()
diff --git a/python/run_dt.bat b/python/run_dt.bat
new file mode 100644
index 0000000..0db0556
--- /dev/null
+++ b/python/run_dt.bat
@@ -0,0 +1,2 @@
+python dtlogger.py
+pause
\ No newline at end of file