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 998a013..0000000
--- a/QuantBox.API.Provider/QuantBox.APIProvider.csproj
+++ /dev/null
@@ -1,151 +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.9.0.1\lib\net45\Newtonsoft.Json.dll
- True
-
-
- ..\packages\NLog.4.3.9\lib\net45\NLog.dll
- True
-
-
- False
- C:\Program Files\SmartQuant Ltd\OpenQuant 2014\SmartQuant.dll
- False
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- False
- C:\Program Files\SmartQuant Ltd\OpenQuant 2014\XAPI_CSharp.exe
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Form
-
-
- ApiControlForm.cs
-
-
- Form
-
-
- ApiManagerForm.cs
-
-
-
-
-
-
-
-
-
-
-
- Designer
-
-
-
-
-
-
-
- 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/ApiItem.cs b/QuantBox.API.Provider/Single/ApiItem.cs
deleted file mode 100644
index 5e75528..0000000
--- a/QuantBox.API.Provider/Single/ApiItem.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-using Newtonsoft.Json;
-using QuantBox.APIProvider.UI;
-using XAPI.Callback;
-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
-{
- ///
- /// 由用户选择Dll,然后加载,得到
- ///
- ///
- public class ApiItem : ICloneable
- {
- public const string CATEGORY_INFO = "Information";
- public const string CATEGORY_Type = "Type";
-
- internal BindingList LinkedUserList;
- internal BindingList LinkedServerList;
-
- private string _dllPath;
-
- [Editor(typeof(System.Windows.Forms.Design.FileNameEditor), typeof(System.Drawing.Design.UITypeEditor))]
- public string DllPath
- {
- get {
- if (ProviderHost.autoMakeRelativePath)
- {
- return PathHelper.MakeRelativePath(_dllPath);
- }
- else
- {
- return PathHelper.MakeAbsolutePath(_dllPath);
- }
- }
- set
- {
- string tmp_dllPath;
- if (ProviderHost.autoMakeRelativePath)
- {
- tmp_dllPath = PathHelper.MakeRelativePath(value);
- }
- else
- {
- tmp_dllPath = PathHelper.MakeAbsolutePath(value);
- }
-
- // 不一样
- bool diff = _dllPath != tmp_dllPath;
- _dllPath = tmp_dllPath;
-
-
- // 这个地方导致Json无法还原,所以先判断是否需要进行调用
- if (LinkedUserList != null) {
- if (Api == null || diff)
- {
- Api = new XApi(_dllPath);
- }
- if (Api != null) {
- try
- {
- Type = Api.GetApiTypes;
- Name = Api.GetApiName;
- Version = Api.GetApiVersion;
-
- // 取公共部分
- UseType = UseType & Type;
- }
- catch(Exception ex)
- {
- Api = null;
- Type = ApiType.Nono;
- Name = ex.Message;
- Version = "请使用depends检查一下是否缺少依赖";
- UseType = ApiType.Nono;
- }
- }
- }
- }
- }
-
- [Browsable(false)]
- internal XApi Api { get; set; }
-
- [Category(CATEGORY_INFO)]
- [ReadOnly(true)]
- public string Name { get; set; }
-
- [Category(CATEGORY_INFO)]
- [ReadOnly(true)]
- public string Version { get; set; }
-
- [TypeConverter(typeof(UserItemConverter))]
- public int User { get; set; }
- [TypeConverter(typeof(ServerItemConverter))]
- public int Server { get; set; }
-
- private BindingList userList = new BindingList();
- public BindingList UserList
- {
- get { return userList; }
- set { userList = value; }
- }
-
- public string LogPrefix { get; set; }
-
-
- [Category(CATEGORY_Type)]
- [ReadOnly(true)]
- public ApiType Type { get; set; }
- [Category(CATEGORY_Type)]
- [Editor(typeof(ApiTypeSelectorEditor), typeof(UITypeEditor))]
- public ApiType UseType { get; set; }
-
-
- public override string ToString()
- {
- return string.Format("LogPrefix={0};UseType={1};Name={2}", LogPrefix, UseType, Name);
- }
-
- public object Clone()
- {
- return this.MemberwiseClone();
- }
- }
-}
diff --git a/QuantBox.API.Provider/Single/OrderMap.cs b/QuantBox.API.Provider/Single/OrderMap.cs
deleted file mode 100644
index 434d470..0000000
--- a/QuantBox.API.Provider/Single/OrderMap.cs
+++ /dev/null
@@ -1,237 +0,0 @@
-using XAPI;
-using SmartQuant;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-using SQ = SmartQuant;
-
-namespace QuantBox.APIProvider.Single
-{
- class OrderMap : BaseMap
- {
- private ConcurrentDictionary externalOrders; // 新单
- // LocalID到Order的映射
- private ConcurrentDictionary pendingOrders; // 新单
- // ID到Order
- private Dictionary workingOrders; // 挂单
-
- // Order.ID与LocalID或ID的映射,没有收到回报时是LocalID,收到后要更新为ID
- private Dictionary orderIDs; // 撤单时映射
- private ConcurrentDictionary pendingCancels; // 撤单拒绝时使用
- private OrderRecord GetExternalOrder(ref TradeField field)
- {
- ExternalOrderRecord record;
- if (externalOrders.TryGetValue(field.InstrumentID, out record)) {
- if (field.OpenClose == OpenCloseType.Open) {
- return field.Side == XAPI.OrderSide.Buy ? record.BuyOpen : record.SellOpen;
- }
- return field.Side == XAPI.OrderSide.Buy ? record.BuyClose : record.SellClose;
- }
- return null;
- }
- private void SetExternalOrder(Order order, ref OrderField field)
- {
- var orderRecord = new OrderRecord(order);
- ExternalOrderRecord record = new ExternalOrderRecord();
- record = externalOrders.GetOrAdd(order.Instrument.Symbol, record);
- if (field.OpenClose == OpenCloseType.Open) {
- if (field.Side == XAPI.OrderSide.Buy)
- {
- record.BuyOpen = orderRecord;
- }
- else {
- record.SellOpen = orderRecord;
- }
- }
- else {
- if (field.Side == XAPI.OrderSide.Buy)
- {
- record.BuyClose = orderRecord;
- }
- else {
- record.SellClose = orderRecord;
- }
- }
- }
- public OrderMap(Framework framework, SingleProvider provider)
- : base(framework, provider)
- {
- externalOrders = new ConcurrentDictionary();
- pendingOrders = new ConcurrentDictionary();
- workingOrders = new Dictionary();
- orderIDs = new Dictionary();
- pendingCancels = new ConcurrentDictionary();
- }
-
- public void Clear()
- {
- pendingOrders.Clear();
- workingOrders.Clear();
- orderIDs.Clear();
- pendingCancels.Clear();
- }
-
- public void DoOrderSend(ref OrderField[] ordersArray, Order order)
- {
- if (Convert.ToInt32(order.Qty) == int.MaxValue) {
- SetExternalOrder(order, ref ordersArray[0]);
- }
- else {
- DoOrderSend(ref ordersArray, new List() { order });
- }
- }
-
- public void DoOrderSend(ref OrderField[] ordersArray, List ordersList)
- {
- // 这里其实返回的是LocalID
- string outstr = provider._TdApi.SendOrder(ref ordersArray);
- string[] OrderIds = outstr.Split(';');
-
- int i = 0;
- foreach (var orderId in OrderIds)
- {
- if (string.IsNullOrEmpty(orderId))
- {
- // 直接将单子拒绝
- EmitExecutionReport(new OrderRecord(ordersList[i]), SQ.ExecType.ExecRejected, SQ.OrderStatus.Rejected, "ErrorCode:" + orderId);
- }
- else
- {
- this.pendingOrders.TryAdd(orderId, new OrderRecord(ordersList[i]));
- // 记下了本地ID,用于立即撤单时供API来定位
- this.orderIDs.Add(ordersList[i].Id, orderId);
- ordersList[i].Fields[9] = orderId;
- }
- ++i;
- }
- }
-
- public void DoOrderCancel(Order order)
- {
- DoOrderCancel(new List() { order });
- }
-
- public void DoOrderCancel(List ordersList)
- {
- OrderRecord[] recordList = new OrderRecord[ordersList.Count];
- string[] OrderIds = new string[ordersList.Count];
-
- for (int i = 0; i < ordersList.Count; ++i) {
- // 如果需要下单的过程中撤单,这里有可能返回LocalID或ID
- if (orderIDs.TryGetValue(ordersList[i].Id, out OrderIds[i])) {
- if (this.workingOrders.TryGetValue(OrderIds[i], out recordList[i])) {
- pendingCancels[OrderIds[i]] = recordList[i];
- }
- }else if (ordersList[i].Fields[9] != null)
- {
- OrderIds[i] = (string)ordersList[i].Fields[9];
- recordList[i] = new OrderRecord(ordersList[i]);
- }
- }
-
- string outstr = provider._TdApi.CancelOrder(OrderIds);
- string[] errs = outstr.Split(';');
-
- {
- int i = 0;
- foreach (var e in errs)
- {
- if (!string.IsNullOrEmpty(e) && e != "0")
- {
- EmitExecutionReport(recordList[i], SQ.ExecType.ExecCancelReject, recordList[i].Order.Status, "ErrorCode:" + e);
- }
- ++i;
- }
- }
- }
-
- public void Process(ref OrderField order)
- {
- // 所有的成交信息都不处理,交给TradeField处理
- if (order.ExecType == XAPI.ExecType.Trade)
- return;
-
- OrderRecord record;
-
- switch (order.ExecType) {
- case XAPI.ExecType.New:
- if (this.pendingOrders.TryRemove(order.LocalID, out record)) {
- this.workingOrders.Add(order.ID, record);
- // 将LocalID更新为ID
- this.orderIDs[record.Order.Id] = order.ID;
- EmitExecutionReport(record, (SQ.ExecType)order.ExecType, (SQ.OrderStatus)order.Status);
- }
- break;
- case XAPI.ExecType.Rejected:
- if (this.pendingOrders.TryRemove(order.LocalID, out record))
- {
- orderIDs.Remove(record.Order.Id);
- EmitExecutionReport(record, (SQ.ExecType)order.ExecType, (SQ.OrderStatus)order.Status, order.Text());
- }
- else if (this.workingOrders.TryGetValue(order.ID, out record)) {
- // 比如说出现超出涨跌停时,先会到ProcessNew,所以得再多判断一次
- workingOrders.Remove(order.ID);
- orderIDs.Remove(record.Order.Id);
- EmitExecutionReport(record, (SQ.ExecType)order.ExecType, (SQ.OrderStatus)order.Status, order.Text());
- }
- break;
- case XAPI.ExecType.Cancelled:
- if (this.workingOrders.TryGetValue(order.ID, out record)) {
- workingOrders.Remove(order.ID);
- orderIDs.Remove(record.Order.Id);
- EmitExecutionReport(record, SQ.ExecType.ExecCancelled, SQ.OrderStatus.Cancelled);
- }
- else if (this.pendingOrders.TryRemove(order.LocalID, out record))
- {
- orderIDs.Remove(record.Order.Id);
- EmitExecutionReport(record, (SQ.ExecType)order.ExecType, (SQ.OrderStatus)order.Status, order.Text());
- }
- break;
- case XAPI.ExecType.PendingCancel:
- if (this.workingOrders.TryGetValue(order.ID, out record)) {
- EmitExecutionReport(record, SQ.ExecType.ExecPendingCancel, SQ.OrderStatus.PendingCancel);
- }
- break;
- case XAPI.ExecType.CancelReject:
- if (this.pendingCancels.TryRemove(order.ID, out record)) {
- EmitExecutionReport(record, SQ.ExecType.ExecCancelReject, (SQ.OrderStatus)order.Status, order.Text());
- }
- break;
- }
- }
-
- public void Process(ref TradeField trade)
- {
- OrderRecord record;
- if (!workingOrders.TryGetValue(trade.ID, out record)) {
- record = GetExternalOrder(ref trade);
- }
- if (record != null) {
- record.AddFill(trade.Price, (int)trade.Qty);
- SQ.ExecType execType = SQ.ExecType.ExecTrade;
- SQ.OrderStatus orderStatus = (record.LeavesQty > 0) ? SQ.OrderStatus.PartiallyFilled : SQ.OrderStatus.Filled;
- ExecutionReport report = CreateReport(record, execType, orderStatus);
- report.LastPx = trade.Price;
- report.LastQty = trade.Qty;
- provider.EmitExecutionReport(report);
- }
- }
-
- public void ProcessNew(ref QuoteField quote, QuoteRecord record)
- {
- OrderRecord askRecord = new OrderRecord(record.AskOrder);
- this.workingOrders.Add(quote.AskID, askRecord);
- this.orderIDs.Add(askRecord.Order.Id, quote.AskID);
- EmitExecutionReport(askRecord, (SQ.ExecType)quote.ExecType, (SQ.OrderStatus)quote.Status);
-
- OrderRecord bidRecord = new OrderRecord(record.BidOrder);
- this.workingOrders.Add(quote.BidID, bidRecord);
- this.orderIDs.Add(bidRecord.Order.Id, quote.BidID);
- EmitExecutionReport(bidRecord, (SQ.ExecType)quote.ExecType, (SQ.OrderStatus)quote.Status);
- }
- }
-}
diff --git a/QuantBox.API.Provider/Single/ServerItem.cs b/QuantBox.API.Provider/Single/ServerItem.cs
deleted file mode 100644
index ddc9a6d..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;
- 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 521a066..0000000
--- a/QuantBox.API.Provider/Single/SessionTimeItem.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-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))]
- 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.MarketData.cs b/QuantBox.API.Provider/Single/SingleProvider.API.MarketData.cs
deleted file mode 100644
index 901ca92..0000000
--- a/QuantBox.API.Provider/Single/SingleProvider.API.MarketData.cs
+++ /dev/null
@@ -1,169 +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 System.Threading.Tasks.Dataflow;
-using QuantBox.Extensions;
-
-namespace QuantBox.APIProvider.Single
-{
- public partial class SingleProvider
- {
- private DateTime _dateTime = DateTime.Now;
- private DateTime _exchangeDateTime = DateTime.Now;
-
- private void OnRtnDepthMarketData_callback(object sender, ref DepthMarketDataNClass pDepthMarketData)
- {
- if (!_enableEmitData)
- return;
-
- try
- {
- // 传过来的Symbol,有可能是不带点,有可能是带点的
- // 要同时存成完整版,因为下单时
- MarketDataRecord record;
- if (!marketDataRecords.TryGetValue(pDepthMarketData.Symbol, out record))
- {
- return;
- }
-
- // 取出上次的行情记录
- DepthMarketDataNClass depthMarket = record.DepthMarket;
-
- //将更新字典的功能提前,因为如果一开始就OnTrade中下单,涨跌停没有更新
- record.DepthMarket = pDepthMarketData;
-
- _dateTime = DateTime.Now;
- try
- {
- _exchangeDateTime = pDepthMarketData.ExchangeDateTime();
- }
- catch
- {
- _exchangeDateTime = _dateTime;
- (sender as XApi).GetLog().Error("{0} ExchangeDateTime有误,现使用LocalDateTime代替,请找API开发人员处理API中的时间兼容问题。", pDepthMarketData.ToFormattedStringExchangeDateTime());
- }
-
-
- if (_emitBidAskFirst)
- {
- if (_emitBidAsk)
- {
- FireBid(record.Instrument.Id, _dateTime, _exchangeDateTime, pDepthMarketData, depthMarket);
- FireAsk(record.Instrument.Id, _dateTime, _exchangeDateTime, pDepthMarketData, depthMarket);
- }
- FireTrade(record.Instrument.Id, _dateTime, _exchangeDateTime, pDepthMarketData, depthMarket);
- }
- else
- {
- FireTrade(record.Instrument.Id, _dateTime, _exchangeDateTime, pDepthMarketData, depthMarket);
- if (_emitBidAsk)
- {
- FireBid(record.Instrument.Id, _dateTime, _exchangeDateTime, pDepthMarketData, depthMarket);
- FireAsk(record.Instrument.Id, _dateTime, _exchangeDateTime, pDepthMarketData, depthMarket);
- }
- }
- }
- catch (Exception ex)
- {
- (sender as XApi).GetLog().Error(ex);
- }
- }
-
- private void FireTrade(int InstrumentId,DateTime _dateTime, DateTime _exchangeDateTime, DepthMarketDataNClass pDepthMarketData,DepthMarketDataNClass DepthMarket)
- {
- //行情过来时是今天累计成交量,得转换成每个tick中成交量之差
- double volume = pDepthMarketData.Volume - DepthMarket.Volume;
- // 以前第一条会导致集合竞价后的第一条没有成交量,这种方法就明确了上一笔是空数据
- if (0 == DepthMarket.TradingDay && 0 == DepthMarket.ActionDay)
- {
- //没有接收到最开始的一条,所以这计算每个Bar的数据时肯定超大,强行设置为0
- volume = 0;
- }
- else if (volume < 0)
- {
- //如果隔夜运行,会出现今早成交量0-昨收盘成交量,出现负数,所以当发现为负时要修改
- volume = pDepthMarketData.Volume;
- }
-
- // 使用新的类,保存更多信息
- var trade = new TradeEx(
- _dateTime,
- _exchangeDateTime,
- this.id,
- InstrumentId,
- pDepthMarketData.LastPrice,
- (int) volume) {DepthMarketData = pDepthMarketData};
-
- // 启用底层数据上传
-
- EmitData(trade);
- }
-
- private void FireBid(int InstrumentId, DateTime _dateTime, DateTime _exchangeDateTime, DepthMarketDataNClass pDepthMarketData, DepthMarketDataNClass DepthMarket)
- {
- do
- {
- if (pDepthMarketData.Bids == null || pDepthMarketData.Bids.Length == 0)
- break;
-
- if (DepthMarket.Bids != null && DepthMarket.Bids.Length > 0)
- {
- if (DepthMarket.Bids[0].Size == pDepthMarketData.Bids[0].Size
- && DepthMarket.Bids[0].Price == pDepthMarketData.Bids[0].Price)
- {
- // 由于与上次一样,不能动
- break;
- }
- }
-
- Bid bid = new Bid(
- _dateTime,
- _exchangeDateTime,
- this.id,
- InstrumentId,
- pDepthMarketData.Bids[0].Price,
- pDepthMarketData.Bids[0].Size);
-
- EmitData(bid);
-
- } while (false);
- }
-
- private void FireAsk(int InstrumentId, DateTime _dateTime, DateTime _exchangeDateTime, DepthMarketDataNClass pDepthMarketData, DepthMarketDataNClass DepthMarket)
- {
- do
- {
- if (pDepthMarketData.Asks == null || pDepthMarketData.Asks.Length == 0)
- break;
-
- if (DepthMarket.Asks != null && DepthMarket.Asks.Length > 0)
- {
- if (DepthMarket.Asks[0].Size == pDepthMarketData.Asks[0].Size
- && DepthMarket.Asks[0].Price == pDepthMarketData.Asks[0].Price)
- {
- // 由于与上次一样,不能动
- break;
- }
-
- }
-
- Ask ask = new Ask(
- _dateTime,
- _exchangeDateTime,
- this.id,
- InstrumentId,
- pDepthMarketData.Asks[0].Price,
- pDepthMarketData.Asks[0].Size);
-
- EmitData(ask);
-
- } while (false);
- }
- }
-}
diff --git a/QuantBox.API.Provider/Single/SingleProvider.API.cs b/QuantBox.API.Provider/Single/SingleProvider.API.cs
deleted file mode 100644
index 97330b1..0000000
--- a/QuantBox.API.Provider/Single/SingleProvider.API.cs
+++ /dev/null
@@ -1,204 +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);
- }
- //记录合约列表,从实盘合约名到对象的映射
- private readonly Dictionary _dictInstruments = 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");
- return;
- }
-
- (sender as XApi).GetLog().Info("OnRspQryTradingAccount:" + account.ToFormattedString());
-
- 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");
- return;
- }
-
- (sender as XApi).GetLog().Info("OnRspQryInvestorPosition:" + position.ToFormattedString());
-
- 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(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");
- return;
- }
-
- (sender as XApi).GetLog().Info("OnRspQryTrade:" + trade.ToFormattedString());
- }
-
- private void OnRspQryOrder_callback(object sender, ref OrderField order, int size1, bool bIsLast)
- {
- if (size1 <= 0)
- {
- (sender as XApi).GetLog().Info("OnRspQryOrder");
- return;
- }
-
- (sender as XApi).GetLog().Info("OnRspQryOrder:" + order.ToFormattedString());
- }
- }
-}
diff --git a/QuantBox.API.Provider/Single/SingleProvider.Other.cs b/QuantBox.API.Provider/Single/SingleProvider.Other.cs
deleted file mode 100644
index 8603ffc..0000000
--- a/QuantBox.API.Provider/Single/SingleProvider.Other.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using XAPI;
-using SmartQuant;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace QuantBox.APIProvider.Single
-{
- public partial class SingleProvider
- {
- // 得到API中的合约名与交易所
- private void GetApi_Symbol_Exchange_TickSize(Instrument instrument,
- out string altSymbol, out string altExchange,
- out string apiSymbol, out string apiExchange,
- out double apiTickSize)
- {
- // 取合约别名
- altSymbol = instrument.GetSymbol(this.id);
- altExchange = instrument.GetExchange(this.id);
- apiTickSize = instrument.TickSize;
-
- // 取合约在API中的名字
- apiSymbol = altSymbol;
- apiExchange = altExchange;
-
- InstrumentField _Instrument;
- if (_dictInstruments.TryGetValue(altSymbol, out _Instrument))
- {
- apiSymbol = _Instrument.InstrumentID;
- apiExchange = _Instrument.ExchangeID;
- apiTickSize = _Instrument.PriceTick;
- }
- }
- }
-}
diff --git a/QuantBox.API.Provider/Single/UserItem.cs b/QuantBox.API.Provider/Single/UserItem.cs
deleted file mode 100644
index 1ea388b..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;
-
- 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 539b72e..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 94%
rename from QuantBox.API.Provider/Properties/AssemblyInfo.cs
rename to QuantBox.APIProvider/Properties/AssemblyInfo.cs
index 30febab..1f25849 100644
--- a/QuantBox.API.Provider/Properties/AssemblyInfo.cs
+++ b/QuantBox.APIProvider/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("0.9.8.4")]
-[assembly: AssemblyFileVersion("0.9.8.4")]
\ No newline at end of file
+[assembly: AssemblyVersion("0.9.8.5")]
+[assembly: AssemblyFileVersion("0.9.8.5")]
\ No newline at end of file
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.APIProvider/Single/ApiItem.cs b/QuantBox.APIProvider/Single/ApiItem.cs
new file mode 100644
index 0000000..10c5cca
--- /dev/null
+++ b/QuantBox.APIProvider/Single/ApiItem.cs
@@ -0,0 +1,121 @@
+using QuantBox.APIProvider.UI;
+using System;
+using System.ComponentModel;
+using XAPI;
+
+namespace QuantBox.APIProvider.Single
+{
+#if NET48
+ using System.Drawing.Design;
+#endif
+ ///
+ /// 由用户选择Dll,然后加载,得到
+ ///
+ ///
+ public class ApiItem : ICloneable
+ {
+ public const string CATEGORY_INFO = "Information";
+ public const string CATEGORY_TYPE = "Type";
+
+ internal BindingList LinkedUserList;
+ internal BindingList LinkedServerList;
+
+ private string _dllPath;
+ private string _typeName;
+
+ private IXApi CheckApi(string typeName, string dllPath)
+ {
+ if (string.IsNullOrEmpty(typeName))
+ {
+ TypeName = "XAPI.Callback.XApi, XAPI_CSharp";
+ return null;
+ }
+ var api = XApiHelper.CreateInstance(typeName, dllPath);
+ try
+ {
+ Type = api.GetApiTypes;
+ Name = api.GetApiName;
+ Version = api.GetApiVersion;
+
+ // 取公共部分
+ UseType = UseType & Type;
+ }
+ catch (Exception ex)
+ {
+ api = null;
+ Type = ApiType.None;
+ Name = ex.Message;
+ Version = "请使用depends检查一下是否缺少依赖";
+ UseType = ApiType.None;
+ }
+ return api;
+ }
+#if NET48
+ [Editor(typeof(System.Windows.Forms.Design.FileNameEditor), typeof(System.Drawing.Design.UITypeEditor))]
+#endif
+ public string DllPath
+ {
+ get
+ {
+ return _dllPath;
+ }
+ set
+ {
+ _dllPath = value;
+ Api = CheckApi(_typeName, _dllPath);
+ }
+ }
+
+ public string TypeName
+ {
+ get
+ {
+ return _typeName;
+ }
+ set
+ {
+ _typeName = value;
+ Api = CheckApi(_typeName, _dllPath);
+ }
+ }
+
+ [Browsable(false)]
+ internal IXApi Api { get; set; }
+
+ [Category(CATEGORY_INFO)]
+ [ReadOnly(true)]
+ public string Name { get; set; }
+
+ [Category(CATEGORY_INFO)]
+ [ReadOnly(true)]
+ public string Version { get; set; }
+
+ [TypeConverter(typeof(UserItemConverter))]
+ public int User { get; set; }
+ [TypeConverter(typeof(ServerItemConverter))]
+ public int Server { get; set; }
+
+ public string LogPrefix { get; set; }
+
+
+ [Category(CATEGORY_TYPE)]
+ [ReadOnly(true)]
+ public ApiType Type { get; set; }
+#if NET48
+ [Editor(typeof(ApiTypeSelectorEditor), typeof(UITypeEditor))]
+#endif
+ [Category(CATEGORY_TYPE)]
+ public ApiType UseType { get; set; }
+
+
+ public override string ToString()
+ {
+ return string.Format("LogPrefix={0};UseType={1};Name={2}", LogPrefix, UseType, Name);
+ }
+
+ public object Clone()
+ {
+ return this.MemberwiseClone();
+ }
+ }
+}
diff --git a/QuantBox.API.Provider/Single/BaseMap.cs b/QuantBox.APIProvider/Single/BaseMap.cs
similarity index 69%
rename from QuantBox.API.Provider/Single/BaseMap.cs
rename to QuantBox.APIProvider/Single/BaseMap.cs
index fbd8ac0..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)
@@ -27,21 +27,10 @@ public ExecutionReport CreateReport(
SQ.ExecType? execType,
SQ.OrderStatus? orderStatus)
{
- ExecutionReport report = new ExecutionReport();
+ ExecutionReport report = new ExecutionReport(record.Order);
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 79%
rename from QuantBox.API.Provider/Single/Extensions.cs
rename to QuantBox.APIProvider/Single/Extensions.cs
index b64743d..ef16f50 100644
--- a/QuantBox.API.Provider/Single/Extensions.cs
+++ b/QuantBox.APIProvider/Single/Extensions.cs
@@ -3,7 +3,7 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using XAPI.Callback;
+using XAPI;
using NLog;
@@ -11,7 +11,7 @@ namespace QuantBox.APIProvider.Single
{
public static class XAPI_Extensions
{
- public static Logger GetLog(this XApi api)
+ public static Logger GetLog(this IXApi api)
{
return (api.Log as Logger);
}
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 76%
rename from QuantBox.API.Provider/Single/MarketDataRecord.cs
rename to QuantBox.APIProvider/Single/MarketDataRecord.cs
index e54420b..34c2584 100644
--- a/QuantBox.API.Provider/Single/MarketDataRecord.cs
+++ b/QuantBox.APIProvider/Single/MarketDataRecord.cs
@@ -16,7 +16,7 @@ public class MarketDataRecord
public string Symbol_Dot;
public string Symbol_Dot_Exchange;
- public Instrument Instrument;
+ public string Instrument;
public bool TradeRequested;
public bool QuoteRequested;
@@ -24,10 +24,12 @@ public class MarketDataRecord
// 记录上次行情
public DepthMarketDataNClass DepthMarket;
- public MarketDataRecord(Instrument instrument)
+ public SortedSet Ids;
+
+ public MarketDataRecord()
{
- this.Instrument = instrument;
- this.DepthMarket = new DepthMarketDataNClass();
+ DepthMarket = new DepthMarketDataNClass();
+ Ids = new SortedSet();
}
}
}
diff --git a/QuantBox.APIProvider/Single/NoTypeConverterJsonConverter.cs b/QuantBox.APIProvider/Single/NoTypeConverterJsonConverter.cs
new file mode 100644
index 0000000..280c123
--- /dev/null
+++ b/QuantBox.APIProvider/Single/NoTypeConverterJsonConverter.cs
@@ -0,0 +1,44 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace QuantBox.APIProvider.Single
+{
+ public class NoTypeConverterJsonConverter : JsonConverter
+ {
+ static readonly IContractResolver resolver = new NoTypeConverterContractResolver();
+
+ class NoTypeConverterContractResolver : DefaultContractResolver
+ {
+ protected override JsonContract CreateContract(Type objectType)
+ {
+ if (typeof(T).IsAssignableFrom(objectType))
+ {
+ var contract = this.CreateObjectContract(objectType);
+ contract.Converter = null; // Also null out the converter to prevent infinite recursion.
+ return contract;
+ }
+ return base.CreateContract(objectType);
+ }
+ }
+
+ public override bool CanConvert(Type objectType)
+ {
+ return typeof(T).IsAssignableFrom(objectType);
+ }
+
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ return JsonSerializer.CreateDefault(new JsonSerializerSettings { ContractResolver = resolver }).Deserialize(reader, objectType);
+ }
+
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ JsonSerializer.CreateDefault(new JsonSerializerSettings { ContractResolver = resolver }).Serialize(writer, value);
+ }
+ }
+}
diff --git a/QuantBox.APIProvider/Single/OrderMap.cs b/QuantBox.APIProvider/Single/OrderMap.cs
new file mode 100644
index 0000000..58d3e83
--- /dev/null
+++ b/QuantBox.APIProvider/Single/OrderMap.cs
@@ -0,0 +1,374 @@
+using XAPI;
+using SmartQuant;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using SQ = SmartQuant;
+
+namespace QuantBox.APIProvider.Single
+{
+ class OrderMap : BaseMap
+ {
+ private ConcurrentDictionary externalOrders; // 新单
+ // LocalID到Order的映射
+ private ConcurrentDictionary pendingOrders; // 新单
+ // ID到Order
+ private Dictionary workingOrders; // 挂单
+
+ // Order.ID与LocalID或ID的映射,没有收到回报时是LocalID,收到后要更新为ID
+ private Dictionary orderIDs; // 撤单时映射
+ private ConcurrentDictionary pendingCancels; // 撤单拒绝时使用
+ private ConcurrentDictionary tradesDict; // 防止重复收到成交回报
+ private OrderRecord GetExternalOrder(ref TradeField field)
+ {
+ ExternalOrderRecord record;
+ if (externalOrders.TryGetValue(field.InstrumentID, out record))
+ {
+ if (field.OpenClose == OpenCloseType.Open)
+ {
+ return field.Side == XAPI.OrderSide.Buy ? record.BuyOpen : record.SellOpen;
+ }
+ return field.Side == XAPI.OrderSide.Buy ? record.BuyClose : record.SellClose;
+ }
+ return null;
+ }
+ private void SetExternalOrder(Order order, ref OrderField field)
+ {
+ var orderRecord = new OrderRecord(order);
+ ExternalOrderRecord record = new ExternalOrderRecord();
+ record = externalOrders.GetOrAdd(order.Instrument.Symbol, record);
+ if (field.OpenClose == OpenCloseType.Open)
+ {
+ if (field.Side == XAPI.OrderSide.Buy)
+ {
+ record.BuyOpen = orderRecord;
+ }
+ else
+ {
+ record.SellOpen = orderRecord;
+ }
+ }
+ else
+ {
+ if (field.Side == XAPI.OrderSide.Buy)
+ {
+ record.BuyClose = orderRecord;
+ }
+ else
+ {
+ record.SellClose = orderRecord;
+ }
+ }
+ }
+ public OrderMap(Framework framework, SingleProvider provider)
+ : base(framework, provider)
+ {
+ externalOrders = new ConcurrentDictionary();
+ pendingOrders = new ConcurrentDictionary();
+ workingOrders = new Dictionary();
+ orderIDs = new Dictionary();
+ pendingCancels = new ConcurrentDictionary();
+ tradesDict = new ConcurrentDictionary();
+ }
+
+ public void Clear()
+ {
+ pendingOrders.Clear();
+ workingOrders.Clear();
+ orderIDs.Clear();
+ pendingCancels.Clear();
+ tradesDict.Clear();
+ }
+
+ public void DoOrderSend(ref OrderField[] ordersArray, Order order)
+ {
+ if (Convert.ToInt32(order.Qty) == int.MaxValue)
+ {
+ SetExternalOrder(order, ref ordersArray[0]);
+ }
+ else
+ {
+ DoOrderSend(ref ordersArray, new List() { order });
+ }
+ }
+
+ public void DoOrderSend(ref OrderField[] ordersArray, List ordersList)
+ {
+ lock (this)
+ {
+ // 这里其实返回的是LocalID
+ string outstr = provider._TdApi.SendOrder(ordersArray);
+ string[] OrderIds = outstr.Split(';');
+
+ int i = 0;
+ foreach (var orderId in OrderIds)
+ {
+ if (string.IsNullOrEmpty(orderId))
+ {
+ // 直接将单子拒绝
+ EmitExecutionReport(new OrderRecord(ordersList[i]), SQ.ExecType.ExecRejected, SQ.OrderStatus.Rejected, "Provider ErrorCode:" + orderId);
+ }
+ else
+ {
+ // 记下了本地ID,用于立即撤单时供API来定位
+ this.orderIDs[ordersList[i].Id] = orderId;
+ //Console.WriteLine(orderId);
+ this.pendingOrders[orderId] = new OrderRecord(ordersList[i]);
+
+ ordersList[i].Fields[9] = orderId;
+ }
+ ++i;
+ }
+ }
+ }
+
+ public void DoOrderCancel(Order order)
+ {
+ DoOrderCancel(new List() { order });
+ }
+
+ public void DoOrderCancel(List ordersList)
+ {
+ lock (this)
+ {
+ OrderRecord[] recordList = new OrderRecord[ordersList.Count];
+ string[] OrderIds = new string[ordersList.Count];
+
+ for (int i = 0; i < ordersList.Count; ++i)
+ {
+ // 如果需要下单的过程中撤单,这里有可能返回LocalID或ID
+ if (orderIDs.TryGetValue(ordersList[i].Id, out OrderIds[i]))
+ {
+ if (this.workingOrders.TryGetValue(OrderIds[i], out recordList[i]))
+ {
+ // 订单已经下到柜台上了
+ pendingCancels[OrderIds[i]] = recordList[i];
+ }
+ else if (this.pendingOrders.TryGetValue(OrderIds[i], out recordList[i]))
+ {
+ // 订单还没有下到柜台,需要撤单
+ pendingCancels[OrderIds[i]] = recordList[i];
+ }
+ }
+ else if (ordersList[i].Fields[9] != null)
+ {
+ OrderIds[i] = (string)ordersList[i].Fields[9];
+ recordList[i] = new OrderRecord(ordersList[i]);
+ }
+ }
+
+ string outstr = provider._TdApi.CancelOrder(OrderIds);
+ string[] errs = outstr.Split(';');
+
+ {
+ int i = 0;
+ foreach (var e in errs)
+ {
+ if (!string.IsNullOrEmpty(e) && e != "0")
+ {
+ if (recordList[i] != null)
+ {
+ EmitExecutionReport(recordList[i], SQ.ExecType.ExecCancelReject, recordList[i].Order.Status, "Provider ErrorCode:" + e);
+ }
+ }
+ ++i;
+ }
+ }
+ }
+ }
+
+ public void Process(ref OrderField order, NLog.Logger log)
+ {
+ // 所有的成交信息都不处理,交给TradeField处理
+ if (order.ExecType == XAPI.ExecType.Trade)
+ return;
+
+ lock (this)
+ {
+ OrderRecord record;
+
+ switch (order.ExecType)
+ {
+ case XAPI.ExecType.New:
+ if (this.pendingOrders.TryRemove(order.LocalID, out record))
+ {
+ this.workingOrders[order.ID] = record;
+ // 将LocalID更新为ID
+ this.orderIDs[record.Order.Id] = order.ID;
+ EmitExecutionReport(record, (SQ.ExecType)order.ExecType, (SQ.OrderStatus)order.Status, order.Text());
+ }
+ else
+ {
+ //log.Warn("New,找不到订单,pendingOrders.Count={0}", pendingOrders.Count);
+ }
+ break;
+ case XAPI.ExecType.Rejected:
+ if (this.pendingOrders.TryRemove(order.LocalID, out record))
+ {
+ orderIDs.Remove(record.Order.Id);
+ record.LeavesQty = 0;
+ EmitExecutionReport(record, (SQ.ExecType)order.ExecType, (SQ.OrderStatus)order.Status, order.Text());
+ }
+ else if (this.workingOrders.TryGetValue(order.ID, out record))
+ {
+ // 比如说出现超出涨跌停时,先会到ProcessNew,所以得再多判断一次
+ workingOrders.Remove(order.ID);
+ orderIDs.Remove(record.Order.Id);
+ record.LeavesQty = 0;
+ EmitExecutionReport(record, (SQ.ExecType)order.ExecType, (SQ.OrderStatus)order.Status, order.Text());
+ }
+ break;
+ case XAPI.ExecType.Cancelled:
+ if (this.workingOrders.TryGetValue(order.ID, out record))
+ {
+ workingOrders.Remove(order.ID);
+ orderIDs.Remove(record.Order.Id);
+ record.LeavesQty = 0;
+ EmitExecutionReport(record, SQ.ExecType.ExecCancelled, SQ.OrderStatus.Cancelled, order.Text());
+ }
+ else if (this.pendingOrders.TryRemove(order.LocalID, out record))
+ {
+ orderIDs.Remove(record.Order.Id);
+ record.LeavesQty = 0;
+ EmitExecutionReport(record, (SQ.ExecType)order.ExecType, (SQ.OrderStatus)order.Status, order.Text());
+ }
+ break;
+ case XAPI.ExecType.PendingCancel:
+ if (this.workingOrders.TryGetValue(order.ID, out record))
+ {
+ EmitExecutionReport(record, SQ.ExecType.ExecPendingCancel, SQ.OrderStatus.PendingCancel);
+ }
+ else if (this.pendingOrders.TryGetValue(order.LocalID, out record))
+ {
+ EmitExecutionReport(record, SQ.ExecType.ExecPendingCancel, SQ.OrderStatus.PendingCancel);
+ }
+ break;
+ case XAPI.ExecType.CancelReject:
+ if (this.pendingCancels.TryRemove(order.ID, out record))
+ {
+ // 已经收到第一回报的情况下
+ EmitExecutionReport(record, SQ.ExecType.ExecCancelReject, (SQ.OrderStatus)order.Status, order.Text());
+ }
+ else if (this.pendingCancels.TryRemove(order.LocalID, out record))
+ {
+ // 没有收到第一条回报的情况下
+ EmitExecutionReport(record, SQ.ExecType.ExecCancelReject, (SQ.OrderStatus)order.Status, order.Text());
+ }
+ //else if (this.workingOrders.TryGetValue(order.ID, out record))
+ //{
+ // // 撤单回报延时的情况下
+ // EmitExecutionReport(record, SQ.ExecType.ExecCancelReject, (SQ.OrderStatus)order.Status, order.Text());
+ //}
+ break;
+ }
+ }
+ }
+
+ public void Process(ref TradeField trade, NLog.Logger log)
+ {
+ lock (this)
+ {
+ OrderRecord record;
+ if (!workingOrders.TryGetValue(trade.ID, out record))
+ {
+ record = GetExternalOrder(ref trade);
+ }
+ if (record != null)
+ {
+ // CTP出现过重复发送委托与成交的情况,需要过滤。并提示
+ // 同一交易ID同一方向不会同时出现,出现表示系统重复提示
+ // 不向方向表示自成交了
+ TradeField field = null;
+ if (tradesDict.TryGetValue(trade.TradeID, out field))
+ {
+ // 有取出,可能是自成交,也可能是重复发单
+ if (trade.Side == field.Side)
+ {
+ log.Error("重复收到成交回报!");
+ // 后面不再处理
+ return;
+ }
+ else
+ {
+ log.Warn("出现自成交!");
+ // 自成交也得处理
+ }
+ }
+ else
+ {
+ tradesDict[trade.TradeID] = trade;
+ }
+
+ record.AddFill(trade.Price, (int)trade.Qty);
+ SQ.ExecType execType = SQ.ExecType.ExecTrade;
+ SQ.OrderStatus orderStatus = (record.LeavesQty > 0) ? SQ.OrderStatus.PartiallyFilled : SQ.OrderStatus.Filled;
+ ExecutionReport report = CreateReport(record, execType, orderStatus);
+ report.LastPx = trade.Price;
+ report.LastQty = trade.Qty;
+ provider.EmitExecutionReport(report);
+ }
+ else
+ {
+ // log.Warn("Trade,找不到订单,workingOrders.Count={0}", workingOrders.Count);
+ }
+ }
+ }
+
+ 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);
+ this.workingOrders[quote.AskID] = askRecord;
+ this.orderIDs[askRecord.Order.Id] = quote.AskID;
+ EmitExecutionReport(askRecord, (SQ.ExecType)quote.ExecType, (SQ.OrderStatus)quote.Status);
+
+ OrderRecord bidRecord = new OrderRecord(record.BidOrder);
+ this.workingOrders[quote.BidID] = bidRecord;
+ this.orderIDs[bidRecord.Order.Id] = quote.BidID;
+ EmitExecutionReport(bidRecord, (SQ.ExecType)quote.ExecType, (SQ.OrderStatus)quote.Status);
+ }
+ }
+}
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 66%
rename from QuantBox.API.Provider/Single/SingleProvider.API.Connection.cs
rename to QuantBox.APIProvider/Single/SingleProvider.API.Connection.cs
index b3f066e..11dbf5d 100644
--- a/QuantBox.API.Provider/Single/SingleProvider.API.Connection.cs
+++ b/QuantBox.APIProvider/Single/SingleProvider.API.Connection.cs
@@ -1,47 +1,44 @@
using SmartQuant;
using System;
-using System.Collections.Generic;
using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-using XAPI.Callback;
using XAPI;
using NLog;
using QuantBox.Extensions;
+using System.Threading;
+using System.IO;
namespace QuantBox.APIProvider.Single
{
-/*
- 插件状态分解
+ /*
+ 插件状态分解
- 人为:
- 用户主动连接,1.连接成功,2连接失败,3,连接成功登录失败,4.登录成功,但初始化失败
- 用户主动断开,
- 定时:
- 定时连接
- 定时断开
- 其它:
- 网络连上
- 网络断开
+ 人为:
+ 用户主动连接,1.连接成功,2连接失败,3,连接成功登录失败,4.登录成功,但初始化失败
+ 用户主动断开,
+ 定时:
+ 定时连接
+ 定时断开
+ 其它:
+ 网络连上
+ 网络断开
- */
-/*
- * OQ里正在断开连接不能轻易用,因为会导致右键时不能连接也不能断开
-*/
+ */
+ /*
+ * OQ里正在断开连接不能轻易用,因为会导致右键时不能连接也不能断开
+ */
public partial class SingleProvider
{
// 实际的连接,由这个来
- internal XApi _TdApi;
- internal XApi _MdApi;
- internal XApi _L2Api;
- internal XApi _QuoteRequestApi;
- internal XApi _HdApi;
- internal XApi _ItApi;
- internal XApi _QueryApi;
+ internal IXApi _TdApi;
+ internal IXApi _MdApi;
+ internal IXApi _L2Api;
+ internal IXApi _QuoteRequestApi;
+ internal IXApi _HdApi;
+ internal IXApi _ItApi;
+ internal IXApi _QueryApi;
private void _Connect(bool bFromUI)
{
@@ -80,7 +77,7 @@ private void _Connect(bool bFromUI)
{
if (item.UseType > 0)
{
- XApi api = ConnectToApi(item);
+ IXApi api = ConnectToApi(item);
assign(item, api);
}
else
@@ -134,78 +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;
+ 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 SessionTimeList.ToList())
+ if (ts < st.SessionStart)
+ {
+ // 停
+ bTryConnect = false;
+ st_next = st;
+ break;
+ }
+ else if (ts <= st.SessionEnd)
{
- // 如果当前时间在交易范围内,要开启重连
- // 如果当前时间不在交易范围内,要主动断开
- TimeSpan ts = e.SignalTime.TimeOfDay;
- 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 = true;
+ st_current = st;
+ break;
}
+ else
+ {
+ // 停
+ 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();
@@ -225,16 +235,16 @@ private void OnConnectionStatus_callback(object sender, ConnectionStatus status,
{
if (userLogin.RawErrorID != 0)
{
- (sender as XApi).GetLog().Info("{0}:{1}", status, userLogin.ToFormattedStringShort());
+ (sender as IXApi).GetLog().Info("{0}:{1}", status, userLogin.ToFormattedStringShort());
}
else
{
- (sender as XApi).GetLog().Info("{0}:{1}", status, userLogin.ToFormattedStringLong());
+ (sender as IXApi).GetLog().Info("{0}:{1}", status, userLogin.ToFormattedStringLong());
}
}
else
{
- (sender as XApi).GetLog().Info("{0}", status);
+ (sender as IXApi).GetLog().Info("{0}", status);
}
switch (status)
@@ -259,7 +269,7 @@ private void OnConnectionStatus_Done(object sender, ConnectionStatus status)
{
if (item.UseType > 0)
{
- if(!IsApiConnected(item.Api))
+ if (!IsApiConnected(item.Api))
{
bCheckOk = false;
break;
@@ -268,10 +278,12 @@ private void OnConnectionStatus_Done(object sender, ConnectionStatus status)
}
// 每个连接都检查是否连上,如果连上,开始进行一些基本的查询
- if(bCheckOk)
+ if (bCheckOk)
{
- base.Status = ProviderStatus.Connected;
+ // 晚一点通知上层,这个更稳定一些
+ // base.Status = ProviderStatus.Connected;
+ // 这个查询不能太快,否则CTP报错
QueryAccountPositionInstrument_Logined();
}
}
@@ -284,7 +296,7 @@ private void OnConnectionStatus_Disconnected(object sender, ConnectionStatus sta
3.主动断开连接
4.被动断开,需要重连
*/
- switch(base.Status)
+ switch (base.Status)
{
case ProviderStatus.Connected:
// 以前连接成功了,现在需要试着重连
@@ -302,7 +314,7 @@ private void OnConnectionStatus_Disconnected(object sender, ConnectionStatus sta
#region XApi小功能
- private void assign(ApiItem item, XApi api)
+ private void assign(ApiItem item, IXApi api)
{
if ((item.UseType & ApiType.MarketData) == ApiType.MarketData)
{
@@ -334,7 +346,7 @@ private void assign(ApiItem item, XApi api)
}
}
- public XApi GetXApi(ApiType type)
+ public IXApi GetXApi(ApiType type)
{
switch (type)
{
@@ -355,35 +367,26 @@ public XApi GetXApi(ApiType type)
}
}
- private XApi ConnectToApi(ApiItem item)
+ private IXApi ConnectToApi(ApiItem item)
{
//lock (this)
{
DisconnectToApi(item);
- XApi api = item.Api;
+ IXApi api = item.Api;
if (api == null)
{
- api = new XApi(PathHelper.MakeAbsolutePath(item.DllPath));
+ api = XApiHelper.CreateInstance(item.TypeName, item.DllPath);
item.Api = api;
}
- api.Server = ServerList[item.Server].ToStruct();
- if (item.UserList.Count > 0)
- {
- foreach (var it in item.UserList)
- {
- api.UserList.Add(it.ToStruct());
- }
- }
- else
- {
- 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;
@@ -412,9 +415,10 @@ private XApi ConnectToApi(ApiItem item)
api.OnRspQryHistoricalTicks = OnRspQryHistoricalTicks_callback;
api.OnRspQryHistoricalBars = OnRspQryHistoricalBars_callback;
- api.OnRspQrySettlementInfo = OnRspQrySettlementInfo;
+ api.OnRspQrySettlementInfo = OnRspQrySettlementInfo_callback;
+ api.OnRtnInstrumentStatus = OnRtnInstrumentStatus_callback;
- api.Connect();
+ api.Connect(szServerPath, szUserPath, Path.GetTempPath());
return api;
}
@@ -423,21 +427,19 @@ private XApi ConnectToApi(ApiItem item)
private void DisconnectToApi(ApiItem item)
{
- if(item.Api != null)
+ if (item.Api != null)
{
// 直接销毁
_DisconnectToApi(item.Api);
-
- //// 在线程中销毁
- //Task task = Task.Factory.StartNew(
- // ()=> { _DisconnectToApi(item.Api); }
- // );
+
+ // 立即断开连接后,登录时自动查资金与持仓的功能在线程中还在运行,会报错
+ assign(item, null);
item.Api = null;
}
}
- private void _DisconnectToApi(XApi api)
+ private void _DisconnectToApi(IXApi api)
{
try
{
@@ -460,7 +462,7 @@ private void _DisconnectToApi(XApi api)
}
}
- private bool IsApiConnected(XApi api)
+ private bool IsApiConnected(IXApi api)
{
return (api != null && api.IsConnected);
}
@@ -492,17 +494,17 @@ private void SetApiReconnectInterval(int reconnectInterval)
#region 其它非关键功能
private void OnRtnError_callback(object sender, ref ErrorField error)
{
- (sender as XApi).GetLog().Error("OnRtnError:" + error.ToFormattedString());
+ (sender as IXApi).GetLog().Error("OnRtnError:" + error.ToFormattedString());
}
private void OnLog_callback(object sender, ref LogField log)
{
- (sender as XApi).GetLog().Info("OnLog:" + log.ToFormattedString());
+ (sender as IXApi).GetLog().Info("OnLog:" + log.ToFormattedString());
}
private void OnRtnQuoteRequest_callback(object sender, ref QuoteRequestField quoteRequest)
{
- (sender as XApi).GetLog().Info("OnRtnQuoteRequest:" + quoteRequest.ToFormattedString());
+ (sender as IXApi).GetLog().Info("OnRtnQuoteRequest:" + quoteRequest.ToFormattedString());
MarketDataRecord record;
if (!marketDataRecords.TryGetValue(quoteRequest.Symbol, out record))
@@ -510,31 +512,55 @@ private void OnRtnQuoteRequest_callback(object sender, ref QuoteRequestField quo
return;
}
- NewsEx news = new NewsEx(DateTime.Now, this.id, record.Instrument.Id, NewsUrgency.Flash, "", "", quoteRequest.ToFormattedString());
- news.ResponseType = XAPI.ResponseType.OnRtnQuoteRequest;
- news.UserData = quoteRequest;
- EmitData(news);
+ foreach (var _id in record.Ids)
+ {
+ NewsEx news = new NewsEx(DateTime.Now, this.id, _id, NewsUrgency.Flash, "", "", quoteRequest.ToFormattedString());
+
+ news.ResponseType = XAPI.ResponseType.OnRtnQuoteRequest;
+ news.UserData = quoteRequest;
+ EmitData(news);
+ }
}
private void QueryAccountPositionInstrument_Logined()
{
- ReqQueryField query = default(ReqQueryField);
+ // 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;
query.PortfolioID2 = DefaultPortfolioID2;
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, ref query);
- _QueryApi.ReqQuery(QueryType.ReqQryInvestorPosition, ref 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, ref query);
+ _dictAccounts_current.Clear();
+ _QueryApi.ReqQuery(QueryType.ReqQryTradingAccount, query);
}
}
@@ -543,25 +569,27 @@ private void QueryAccountPosition_OnTimer()
if (!IsApiConnected(_QueryApi))
return;
- ReqQueryField query = default(ReqQueryField);
+ ReqQueryField query = new ReqQueryField();
query.PortfolioID1 = DefaultPortfolioID1;
query.PortfolioID2 = DefaultPortfolioID2;
query.PortfolioID3 = DefaultPortfolioID3;
query.Business = DefaultBusiness;
- _QueryAccountCount -= (int)_Timer.Interval / 1000;
- if (_QueryAccountCount <= 0)
- {
- _QueryApi.ReqQuery(QueryType.ReqQryTradingAccount, ref query);
- _QueryAccountCount = _QueryAccountInterval;
- }
-
_QueryPositionCount -= (int)_Timer.Interval / 1000;
if (_QueryPositionCount <= 0)
{
- _QueryApi.ReqQuery(QueryType.ReqQryInvestorPosition, ref query);
+ _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 998543a..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(EnableEmitHistoricalData)
+ if(EnablEmitHistoricalData)
{
HistoricalData data = new HistoricalData
{
@@ -217,7 +217,7 @@ private void OnRspQryHistoricalBars_callback(object sender, IntPtr pBars, int si
}
}
- if(EnableEmitHistoricalData)
+ if(EnablEmitHistoricalData)
{
HistoricalData data = new HistoricalData
{
diff --git a/QuantBox.APIProvider/Single/SingleProvider.API.MarketData.cs b/QuantBox.APIProvider/Single/SingleProvider.API.MarketData.cs
new file mode 100644
index 0000000..28ba4ce
--- /dev/null
+++ b/QuantBox.APIProvider/Single/SingleProvider.API.MarketData.cs
@@ -0,0 +1,255 @@
+using SmartQuant;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using XAPI.Callback;
+using XAPI;
+using QuantBox.Extensions;
+
+namespace QuantBox.APIProvider.Single
+{
+ public partial class SingleProvider
+ {
+ private DateTime _dateTime = DateTime.Now;
+ private DateTime _exchangeDateTime = DateTime.Now;
+
+ private void OnRtnDepthMarketData_callback(object sender, ref DepthMarketDataNClass pDepthMarketData)
+ {
+ if (!_enableEmitData)
+ return;
+
+ try
+ {
+ // 传过来的Symbol,有可能是不带点,有可能是带点的
+ // 要同时存成完整版,因为下单时
+ MarketDataRecord record;
+ if (!marketDataRecords.TryGetValue(pDepthMarketData.Symbol, out record))
+ {
+ return;
+ }
+
+ // 取出上次的行情记录
+ DepthMarketDataNClass depthMarket = record.DepthMarket;
+
+ //将更新字典的功能提前,因为如果一开始就OnTrade中下单,涨跌停没有更新
+ record.DepthMarket = pDepthMarketData;
+
+ _dateTime = DateTime.Now;
+ try
+ {
+ _exchangeDateTime = pDepthMarketData.ExchangeDateTime();
+ }
+ catch
+ {
+ _exchangeDateTime = _dateTime;
+ (sender as XApi).GetLog().Error("{0} ExchangeDateTime有误,现使用LocalDateTime代替,请找API开发人员处理API中的时间兼容问题。", pDepthMarketData.ToFormattedStringExchangeDateTime());
+ }
+
+
+ if (_emitBidAskFirst)
+ {
+ if (_emitBidAsk)
+ {
+ FireBid(record.Ids, _dateTime, _exchangeDateTime, pDepthMarketData, depthMarket);
+ FireAsk(record.Ids, _dateTime, _exchangeDateTime, pDepthMarketData, depthMarket);
+ }
+ FireTrade(record.Ids, _dateTime, _exchangeDateTime, pDepthMarketData, depthMarket);
+ }
+ else
+ {
+ FireTrade(record.Ids, _dateTime, _exchangeDateTime, pDepthMarketData, depthMarket);
+ if (_emitBidAsk)
+ {
+ FireBid(record.Ids, _dateTime, _exchangeDateTime, pDepthMarketData, depthMarket);
+ FireAsk(record.Ids, _dateTime, _exchangeDateTime, pDepthMarketData, depthMarket);
+ }
+ }
+
+ if (_emitLevel2Snapshot)
+ {
+ FireLevel2Snapshot(record.Ids, _dateTime, _exchangeDateTime, pDepthMarketData, depthMarket);
+ }
+ }
+ catch (Exception ex)
+ {
+ (sender as XApi).GetLog().Error(ex);
+ }
+ }
+
+ private void FireTrade(SortedSet Ids, DateTime _dateTime, DateTime _exchangeDateTime, DepthMarketDataNClass pDepthMarketData, DepthMarketDataNClass DepthMarket)
+ {
+ //行情过来时是今天累计成交量,得转换成每个tick中成交量之差
+ double volume = pDepthMarketData.Volume - DepthMarket.Volume;
+ // 以前第一条会导致集合竞价后的第一条没有成交量,这种方法就明确了上一笔是空数据
+ if (0 == DepthMarket.TradingDay && 0 == DepthMarket.ActionDay)
+ {
+ //没有接收到最开始的一条,所以这计算每个Bar的数据时肯定超大,强行设置为0
+ volume = 0;
+ }
+ else if (volume < 0)
+ {
+ //如果隔夜运行,会出现今早成交量0-昨收盘成交量,出现负数,所以当发现为负时要修改
+ volume = pDepthMarketData.Volume;
+ }
+
+ foreach (var _id in Ids)
+ {
+ // 使用新的类,保存更多信息
+ var trade = new TradeEx(
+ _dateTime,
+ _exchangeDateTime,
+ id,
+ _id,
+ pDepthMarketData.LastPrice,
+ (int)volume)
+ {
+ DepthMarketData = pDepthMarketData
+ };
+
+ // 启用底层数据上传
+ EmitData(trade);
+ }
+ }
+
+ private void FireLevel2Snapshot(SortedSet Ids, DateTime _dateTime, DateTime _exchangeDateTime, DepthMarketDataNClass pDepthMarketData, DepthMarketDataNClass DepthMarket)
+ {
+ //行情过来时是今天累计成交量,得转换成每个tick中成交量之差
+ double volume = pDepthMarketData.Volume - DepthMarket.Volume;
+ // 以前第一条会导致集合竞价后的第一条没有成交量,这种方法就明确了上一笔是空数据
+ if (0 == DepthMarket.TradingDay && 0 == DepthMarket.ActionDay)
+ {
+ //没有接收到最开始的一条,所以这计算每个Bar的数据时肯定超大,强行设置为0
+ volume = 0;
+ }
+ else if (volume < 0)
+ {
+ //如果隔夜运行,会出现今早成交量0-昨收盘成交量,出现负数,所以当发现为负时要修改
+ volume = pDepthMarketData.Volume;
+ }
+
+ foreach (var _id in Ids)
+ {
+ List bids = new List();
+ if (pDepthMarketData.Bids != null)
+ {
+ foreach (var d in pDepthMarketData.Bids)
+ {
+ Bid bid = new Bid(
+ _dateTime,
+ _exchangeDateTime,
+ id,
+ _id,
+ d.Price,
+ d.Size);
+ bids.Add(bid);
+ }
+ }
+
+ List asks = new List();
+ if (pDepthMarketData.Asks != null)
+ {
+ foreach (var d in pDepthMarketData.Asks)
+ {
+ Ask ask = new Ask(
+ _dateTime,
+ _exchangeDateTime,
+ id,
+ _id,
+ d.Price,
+ d.Size);
+ asks.Add(ask);
+ }
+ }
+
+ var l2s = new Level2Snapshot(_dateTime, _exchangeDateTime, id, _id, bids.ToArray(), asks.ToArray())
+ {
+
+ };
+
+ // 启用底层数据上传
+ EmitData(l2s);
+ }
+ }
+
+ private void FireBid(SortedSet Ids, DateTime _dateTime, DateTime _exchangeDateTime, DepthMarketDataNClass pDepthMarketData, DepthMarketDataNClass DepthMarket)
+ {
+ double price = 0.0;
+ int size = 0;
+
+ // 当出现涨跌停时,有可能是一开始就是涨跌停,也有可能慢慢变成涨跌停
+ if(pDepthMarketData.Bids == null || pDepthMarketData.Bids.Length == 0)
+ {
+ }
+ else
+ {
+ if (DepthMarket.Bids != null && DepthMarket.Bids.Length > 0)
+ {
+ if (DepthMarket.Bids[0].Size == pDepthMarketData.Bids[0].Size
+ && DepthMarket.Bids[0].Price == pDepthMarketData.Bids[0].Price)
+ {
+ // 由于与上次一样,不能动
+ return;
+ }
+ }
+
+ price = pDepthMarketData.Bids[0].Price;
+ size = pDepthMarketData.Bids[0].Size;
+ }
+
+ foreach (var _id in Ids)
+ {
+ Bid bid = new Bid(
+ _dateTime,
+ _exchangeDateTime,
+ id,
+ _id,
+ price,
+ size);
+
+ EmitData(bid);
+ }
+ }
+
+ private void FireAsk(SortedSet Ids, DateTime _dateTime, DateTime _exchangeDateTime, DepthMarketDataNClass pDepthMarketData, DepthMarketDataNClass DepthMarket)
+ {
+ double price = 0.0;
+ int size = 0;
+
+ // 当出现涨跌停时,有可能是一开始就是涨跌停,也有可能慢慢变成涨跌停
+ if (pDepthMarketData.Asks == null || pDepthMarketData.Asks.Length == 0)
+ {
+ }
+ else
+ {
+ if (DepthMarket.Asks != null && DepthMarket.Asks.Length > 0)
+ {
+ if (DepthMarket.Asks[0].Size == pDepthMarketData.Asks[0].Size
+ && DepthMarket.Asks[0].Price == pDepthMarketData.Asks[0].Price)
+ {
+ // 由于与上次一样,不能动
+ return;
+ }
+ }
+
+ price = pDepthMarketData.Asks[0].Price;
+ size = pDepthMarketData.Asks[0].Size;
+ }
+
+ foreach (var _id in Ids)
+ {
+ Ask ask = new Ask(
+ _dateTime,
+ _exchangeDateTime,
+ id,
+ _id,
+ price,
+ size);
+
+ EmitData(ask);
+ }
+ }
+ }
+}
diff --git a/QuantBox.API.Provider/Single/SingleProvider.API.Order.cs b/QuantBox.APIProvider/Single/SingleProvider.API.Order.cs
similarity index 73%
rename from QuantBox.API.Provider/Single/SingleProvider.API.Order.cs
rename to QuantBox.APIProvider/Single/SingleProvider.API.Order.cs
index 64b5a64..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;
@@ -18,47 +13,7 @@ namespace QuantBox.APIProvider.Single
public partial class SingleProvider
{
#region 价格修正
- //public double FixPrice(double price, SmartQuant.OrderSide Side, double tickSize, double LowerLimitPrice, double UpperLimitPrice)
- //{
- // //没有设置就直接用
- // if (tickSize > 0)
- // {
- // decimal remainder = ((decimal)price % (decimal)tickSize);
- // if (remainder != 0)
- // {
- // if (Side == SmartQuant.OrderSide.Buy)
- // {
- // price = Math.Round(Math.Ceiling(price / tickSize) * tickSize, 6);
- // }
- // else
- // {
- // price = Math.Round(Math.Floor(price / tickSize) * tickSize, 6);
- // }
- // }
- // else
- // {
- // //正好能整除,不操作
- // }
- // }
-
- // if (0 == UpperLimitPrice
- // && 0 == LowerLimitPrice)
- // {
- // //涨跌停无效
-
- // }
- // else
- // {
- // //防止价格超过涨跌停
- // if (price >= UpperLimitPrice)
- // price = UpperLimitPrice;
- // else if (price <= LowerLimitPrice)
- // price = LowerLimitPrice;
- // }
- // return price;
- //}
-
- public double FixPrice(MarketDataRecord record,double price, SmartQuant.OrderSide Side, double tickSize)
+ public double FixPrice(MarketDataRecord record, double price, SmartQuant.OrderSide Side, double tickSize)
{
double LowerLimitPrice = record.DepthMarket.LowerLimitPrice;
double UpperLimitPrice = record.DepthMarket.UpperLimitPrice;
@@ -89,7 +44,7 @@ public double FixPrice(MarketDataRecord record,double price, SmartQuant.OrderSid
{
//涨跌停无效
_TdApi.GetLog().Warn("Symbol:{0},Symbol_Dot_Exchange:{1},LowerLimitPrice && UpperLimitPrice 为0,没有进行价格修正",
- record.Symbol,record.Symbol_Dot_Exchange);
+ record.Symbol, record.Symbol_Dot_Exchange);
}
else
{
@@ -183,11 +138,13 @@ private void CmdNewOrderSingle(ExecutionCommand command)
string apiSymbol;
string apiExchange;
double apiTickSize;
+ string apiProductID;
- GetApi_Symbol_Exchange_TickSize(command.Instrument,
+ 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];
@@ -198,7 +155,7 @@ private void CmdNewOrderSingle(ExecutionCommand command)
MarketDataRecord record;
if (marketDataRecords.TryGetValue(command.Instrument.Symbol, out record))
{
- switch(command.OrdType)
+ switch (command.OrdType)
{
case SQ.OrderType.Market:
case SQ.OrderType.MarketOnClose:
@@ -215,7 +172,7 @@ private void CmdNewOrderSingle(ExecutionCommand command)
}
// 市价单使用限价单模拟
- if(SwitchMakertOrderToLimitOrder)
+ if (SwitchMakertOrderToLimitOrder)
{
fields[0].Type = XAPI.OrderType.Limit;
}
@@ -224,10 +181,9 @@ private void CmdNewOrderSingle(ExecutionCommand command)
}
if (HasPriceLimit)
{
- //price = FixPrice(price, command.Side, apiTickSize, record.DepthMarket.LowerLimitPrice, record.DepthMarket.UpperLimitPrice);
- price = FixPrice(record,price, command.Side, apiTickSize);
+ price = FixPrice(record, price, command.Side, apiTickSize);
}
-
+
fields[0].Price = price;
}
@@ -247,11 +203,13 @@ private void CmdNewOrderList(ExecutionCommand command)
string apiSymbol;
string apiExchange;
double apiTickSize;
+ string apiProductID;
- GetApi_Symbol_Exchange_TickSize(orders[i].Instrument,
+ 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);
}
@@ -261,6 +219,8 @@ private void CmdNewOrderList(ExecutionCommand command)
private void ToOrderStruct(ref OrderField field, Order order, string apiSymbol, string apiExchange)
{
+ field = new OrderField();
+
field.InstrumentID = apiSymbol;
field.ExchangeID = apiExchange;
field.Price = order.Price;
@@ -278,31 +238,71 @@ private void ToOrderStruct(ref OrderField field, Order order, string apiSymbol,
field.PortfolioID2 = GetPortfolioID2(order);
field.PortfolioID3 = GetPortfolioID3(order);
field.Business = GetBusiness(order);
+
+ //OpenClose2SubSide(ref field, order);
}
private void OnRtnOrder_callback(object sender, ref OrderField order)
{
- (sender as XApi).GetLog().Debug("OnRtnOrder:" + order.ToFormattedString());
- try
+ lock (this)
{
- orderMap.Process(ref order);
- }
- catch (Exception ex)
- {
- (sender as XApi).GetLog().Error(ex);
+ var log = (sender as XApi).GetLog();
+ log.Debug("OnRtnOrder:" + order.ToFormattedString());
+
+ // 由策略来收回报
+ if (OnRtnOrder != null)
+ OnRtnOrder(sender, ref order);
+
+ try
+ {
+ orderMap.Process(ref order, log);
+ }
+ catch (Exception ex)
+ {
+ log.Error(ex);
+ }
}
}
private void OnRtnTrade_callback(object sender, ref TradeField trade)
{
- (sender as XApi).GetLog().Debug("OnRtnTrade:" + trade.ToFormattedString());
- try
+ lock (this)
{
- orderMap.Process(ref trade);
+ var log = (sender as XApi).GetLog();
+ log.Debug("OnRtnTrade:" + trade.ToFormattedString());
+
+ // 由策略来收回报
+ if (OnRtnTrade != null)
+ OnRtnTrade(sender, ref trade);
+
+ try
+ {
+ orderMap.Process(ref trade, log);
+ }
+ catch (Exception ex)
+ {
+ log.Error(ex);
+ }
}
- catch (Exception ex)
+ }
+
+ private void OnRtnOrder_Expired(ref InstrumentStatusField instrumentStatus)
+ {
+ if (_TdApi == null)
+ return;
+
+ lock (this)
{
- (sender as XApi).GetLog().Error(ex);
+ 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 85%
rename from QuantBox.API.Provider/Single/SingleProvider.API.Quote.cs
rename to QuantBox.APIProvider/Single/SingleProvider.API.Quote.cs
index 7ce52ad..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,
+ 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;
@@ -69,10 +64,12 @@ private void CmdCancelQuote(ExecutionCommand command)
private void OnRtnQuote_callback(object sender, ref QuoteField quote)
{
(sender as XApi).GetLog().Debug("OnRtnQuote:" + quote.ToFormattedString());
- try {
+ try
+ {
quoteMap.Process(ref quote);
}
- catch (Exception ex) {
+ catch (Exception ex)
+ {
(sender as XApi).GetLog().Error(ex);
}
}
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 64%
rename from QuantBox.API.Provider/Single/SingleProvider.DataProvider.cs
rename to QuantBox.APIProvider/Single/SingleProvider.DataProvider.cs
index 6d93f1b..caa93bf 100644
--- a/QuantBox.API.Provider/Single/SingleProvider.DataProvider.cs
+++ b/QuantBox.APIProvider/Single/SingleProvider.DataProvider.cs
@@ -16,7 +16,7 @@ private void SubscribeForTest(Instrument instrument)
{
Bid bid = new Bid(DateTime.Now, this.id, instrument.Id, 100, 5);
EmitData(bid);
-
+
Ask ask = new Ask(DateTime.Now, this.id, instrument.Id, 101, 5);
EmitData(ask);
}
@@ -40,21 +40,30 @@ public override void Subscribe(Instrument instrument)
string apiSymbol;
string apiExchange;
double apiTickSize;
+ string apiProductID;
- GetApi_Symbol_Exchange_TickSize(instrument,
+ 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);
string Symbol_Dot = string.Format("{0}.", apiSymbol);
string Symbol = apiSymbol;
MarketDataRecord record;
+
if (!marketDataRecords.TryGetValue(Symbol_Dot_Exchange, out record))
{
- record = new MarketDataRecord(instrument);
- record.Symbol = Symbol;
+ record = new MarketDataRecord();
+
+ record.TradeRequested = true;
+ record.QuoteRequested = true;
+ record.MarketDepthRequested = true;
+
+ record.Symbol = apiSymbol;
record.Exchange = apiExchange;
record.Symbol_Dot = Symbol_Dot;
record.Symbol_Dot_Exchange = Symbol_Dot_Exchange;
@@ -63,13 +72,15 @@ public override void Subscribe(Instrument instrument)
marketDataRecords[Symbol_Dot_Exchange] = record;
marketDataRecords[Symbol_Dot] = record;
marketDataRecords[Symbol] = record;
- }
-
- record.TradeRequested = true;
- record.QuoteRequested = true;
- record.MarketDepthRequested = true;
- Subscribe(record);
+ record.Instrument = instrument.Symbol;
+ record.Ids.Add(instrument.Id);
+ Subscribe(record);
+ }
+ else
+ {
+ record.Ids.Add(instrument.Id);
+ }
}
public override void Unsubscribe(Instrument instrument)
@@ -79,11 +90,13 @@ public override void Unsubscribe(Instrument instrument)
string apiSymbol;
string apiExchange;
double apiTickSize;
+ string apiProductID;
- GetApi_Symbol_Exchange_TickSize(instrument,
+ 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);
@@ -93,25 +106,30 @@ public override void Unsubscribe(Instrument instrument)
MarketDataRecord record;
if (marketDataRecords.TryGetValue(Symbol_Dot_Exchange, out record))
{
- Unsubscribe(record);
-
- // 多次订阅也无所谓
- record.TradeRequested = false;
- record.QuoteRequested = false;
- record.MarketDepthRequested = false;
+ record.Ids.Remove(instrument.Id);
+
+ if (record.Ids.Count == 0)
+ {
+ Unsubscribe(record);
+
+ // 多次订阅也无所谓
+ record.TradeRequested = false;
+ record.QuoteRequested = false;
+ record.MarketDepthRequested = false;
+
+ // 移除
+ marketDataRecords.Remove(Symbol_Dot_Exchange);
+ marketDataRecords.Remove(Symbol_Dot);
+ marketDataRecords.Remove(Symbol);
+ }
}
-
- // 移除
- marketDataRecords.Remove(Symbol_Dot_Exchange);
- marketDataRecords.Remove(Symbol_Dot);
- marketDataRecords.Remove(Symbol);
}
private void Subscribe(MarketDataRecord record)
{
if (IsApiConnected(_MdApi))
{
- _MdApi.GetLog().Info("订阅合约 {0} {1} {2}", record.Instrument.Symbol, record.Symbol, record.Exchange);
+ _MdApi.GetLog().Info("订阅合约:Symbol:{0};InstrumentID:{1};ExchangeID:{2}", record.Instrument, record.Symbol, record.Exchange);
_MdApi.Subscribe(record.Symbol, record.Exchange);
}
else
@@ -128,15 +146,16 @@ private void Subscribe(MarketDataRecord record)
private void Unsubscribe(MarketDataRecord record)
{
- if (_MdApi != null)
+ if (IsApiConnected(_MdApi))
{
- _MdApi.GetLog().Info("退订合约 {0} {1} {2}", record.Instrument.Symbol, record.Symbol, record.Exchange);
+ _MdApi.GetLog().Info("退订合约:Symbol:{0};InstrumentID:{1};ExchangeID:{2}", record.Instrument, record.Symbol, record.Exchange);
_MdApi.Unsubscribe(record.Symbol, record.Exchange);
}
- if (_QuoteRequestApi != null && SubscribeQuote)
+ if (SubscribeQuote)
{
- _QuoteRequestApi.UnsubscribeQuote(record.Symbol, record.Exchange);
+ if (IsApiConnected(_QuoteRequestApi))
+ _QuoteRequestApi.UnsubscribeQuote(record.Symbol, record.Exchange);
}
}
}
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 89%
rename from QuantBox.API.Provider/Single/SingleProvider.HistoricalDataProvider.cs
rename to QuantBox.APIProvider/Single/SingleProvider.HistoricalDataProvider.cs
index e111a04..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,
+ 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;
@@ -34,9 +36,9 @@ private HistoricalDataRequestField ToStruct(HistoricalDataRequest request)
field.Time1 = GetTime(request.DateTime1);
field.Time2 = GetTime(request.DateTime2);
field.DataType = (XAPI.DataObjetType)request.DataType;
- if(request.BarType.HasValue)
+ if (request.BarType.HasValue)
field.BarType = (XAPI.BarType)request.BarType.Value;
- if(request.BarSize.HasValue)
+ if (request.BarSize.HasValue)
field.BarSize = (int)request.BarSize.Value;
//field.RequestId;
//field.Count;
@@ -52,7 +54,7 @@ void IHistoricalDataProvider.Cancel(string requestId)
}
public override void Send(HistoricalDataRequest request)
{
- if(!IsApiConnected(_HdApi))
+ if (!IsApiConnected(_HdApi))
{
EmitHistoricalDataEnd(request.RequestId, RequestResult.Error, "Provider is not connected.");
xlog.Error("历史行情服务器没有连接");
@@ -60,7 +62,7 @@ public override void Send(HistoricalDataRequest request)
}
int iRet = 1;
- switch(request.DataType)
+ switch (request.DataType)
{
case DataObjectType.Bid:
case DataObjectType.Ask:
diff --git a/QuantBox.API.Provider/Single/SingleProvider.InstrumentProvider.cs b/QuantBox.APIProvider/Single/SingleProvider.InstrumentProvider.cs
similarity index 93%
rename from QuantBox.API.Provider/Single/SingleProvider.InstrumentProvider.cs
rename to QuantBox.APIProvider/Single/SingleProvider.InstrumentProvider.cs
index 68dc26c..f6794c0 100644
--- a/QuantBox.API.Provider/Single/SingleProvider.InstrumentProvider.cs
+++ b/QuantBox.APIProvider/Single/SingleProvider.InstrumentProvider.cs
@@ -20,7 +20,7 @@ public override void Send(InstrumentDefinitionRequest request)
private void ReturnInstrumentDefinition(InstrumentDefinitionRequest request)
{
- Dictionary> dict = new Dictionary>();
+ Dictionary> dict = new Dictionary>();
List instruments = new List();
@@ -92,19 +92,19 @@ private void ReturnInstrumentDefinition(InstrumentDefinitionRequest request)
description.Underlying = contract.UnderlyingInstrID;
instrument.Description = JsonConvert.SerializeObject(description, description.GetType(), null);
-
- if(!string.IsNullOrWhiteSpace(contract.UnderlyingInstrID))
+
+ if (!string.IsNullOrWhiteSpace(contract.UnderlyingInstrID))
{
// 要求先导入标的合约,再导入期权,如果在这里生成会丢失合约信息
Instrument UnderlyingInstrID = framework.InstrumentManager.Get(contract.UnderlyingInstrID);
- if(UnderlyingInstrID == null)
+ if (UnderlyingInstrID == null)
{
//xlog.Warn("合约:{0},存在标的物字段,请先导入标的物合约{1},导入后放在Parent属性中", contract.Symbol, contract.UnderlyingInstrID);
List list = null;
- if(!dict.TryGetValue(contract.UnderlyingInstrID,out list))
+ if (!dict.TryGetValue(contract.UnderlyingInstrID, out list))
{
list = new List();
- dict.Add(contract.UnderlyingInstrID,list);
+ dict.Add(contract.UnderlyingInstrID, list);
}
list.Add(contract.Symbol);
}
@@ -120,6 +120,8 @@ private void ReturnInstrumentDefinition(InstrumentDefinitionRequest request)
int yyyy = contract.ExpireDate / 10000;
int MM = contract.ExpireDate % 10000 / 100;
int dd = contract.ExpireDate % 100;
+ // 居然有期权传回来的到期时间没有日
+ dd = Math.Max(dd, 1);
instrument.Maturity = new DateTime(yyyy, MM, dd);
}
@@ -131,7 +133,7 @@ private void ReturnInstrumentDefinition(InstrumentDefinitionRequest request)
instruments.Add(instrument);
}
- if(dict.Count>0)
+ if (dict.Count > 0)
{
xlog.Warn("标的物合约必须先导入,然后再Request,衍生品合约的Legs才会有一条记录指向标的物");
foreach (var kv in dict)
@@ -141,7 +143,7 @@ private void ReturnInstrumentDefinition(InstrumentDefinitionRequest request)
xlog.Warn("请将以上合约先导入");
xlog.Warn("如果在Request后列表中没有标的物合约,则需要手工添加");
}
-
+
instruments.Sort(SortInstrument);
diff --git a/QuantBox.APIProvider/Single/SingleProvider.Other.cs b/QuantBox.APIProvider/Single/SingleProvider.Other.cs
new file mode 100644
index 0000000..137b9fc
--- /dev/null
+++ b/QuantBox.APIProvider/Single/SingleProvider.Other.cs
@@ -0,0 +1,45 @@
+using XAPI;
+using SmartQuant;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace QuantBox.APIProvider.Single
+{
+ public partial class SingleProvider
+ {
+ // 得到API中的合约名与交易所
+ 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 string apiProductID)
+ {
+ // 取合约别名
+ altSymbol = instrument.GetSymbol(id);
+ altExchange = instrument.GetExchange(id);
+ apiTickSize = instrument.TickSize;
+
+ // 取合约在API中的名字
+ apiSymbol = altSymbol;
+ apiExchange = altExchange;
+ apiProductID = "";
+
+ // 对于UFX,没有实现查询合约的功能,所以这里其实使用的是AltID中的信息
+ // 屏蔽这个功能,订阅的合约就根据设置来了
+ if(true)
+ {
+ InstrumentField _Instrument;
+ if (_dictInstruments.TryGetValue(altSymbol, out _Instrument))
+ {
+ 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 92%
rename from QuantBox.API.Provider/Single/SingleProvider.Provider.cs
rename to QuantBox.APIProvider/Single/SingleProvider.Provider.cs
index 8b9694a..9ef770a 100644
--- a/QuantBox.API.Provider/Single/SingleProvider.Provider.cs
+++ b/QuantBox.APIProvider/Single/SingleProvider.Provider.cs
@@ -11,14 +11,13 @@
using XAPI;
using XAPI.Callback;
-//using System.Threading.Tasks.Dataflow;
-
-
namespace QuantBox.APIProvider.Single
{
public partial class SingleProvider:Provider
{
private Logger xlog;
+ private Logger alog;
+ private Logger plog;
private Logger barLog;
private Logger tickLog;
@@ -38,7 +37,8 @@ public SingleProvider(Framework framework)
~SingleProvider()
{
- Save();
+ // OQ多开时,其中一个改了,另一个关闭会导致保存丢失
+ // Save();
}
private static JsonSerializerSettings jSetting = new JsonSerializerSettings()
@@ -58,14 +58,17 @@ 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");
// 以下初始化的值在,初始化后由软件读取文件中的参数据后又设置回来
LastPricePlusNTicks = 10;
EmitBidAsk = true;
+ EmitLevel2Snapshot = false;
//UpdateInstrument = true;
- EnableEmitHistoricalData = true;
+ EnablEmitHistoricalData = true;
FilterDateTime = true;
EnableEmitData = true;
HasPriceLimit = true;
@@ -139,7 +142,7 @@ private void Save(string path,string file,object obj)
}
}
- internal void Save()
+ public void Save()
{
Save(ConfigPath, "SessionTimeList.json", SessionTimeList);
Save(ConfigPath, "ServerList.json", ServerList);
@@ -147,7 +150,7 @@ internal void Save()
Save(ConfigPath, "ApiList.json", ApiList);
}
- private void Load()
+ public void Load()
{
SessionTimeList = new BindingList();
UserList = new BindingList();
@@ -188,22 +191,24 @@ private void Load()
private int _QueryAccountCount = 0;
private int _QueryPositionCount = 0;
- public override void Connect()
+ protected override void OnConnect()
{
+ Load();
+
_QueryAccountCount = _QueryAccountInterval;
_QueryPositionCount = _QueryPositionInterval;
// 启动重连定时器
_Timer.Elapsed -= _Timer_Elapsed;
// 改小用来测试连接销毁,用完要改回去
- _Timer.Interval = 20 * 1000;
+ _Timer.Interval = 30 * 1000;
_Timer.Enabled = true;
_Timer.Elapsed += _Timer_Elapsed;
xlog.Info("重连检测定时器开启,检测频率(毫秒):{0}", _Timer.Interval);
_Connect(true);
}
- public override void Disconnect()
+ protected override void OnDisconnect()
{
// 关闭重连定时器
_Timer.Elapsed -= _Timer_Elapsed;
@@ -212,6 +217,8 @@ public override void Disconnect()
xlog.Info("重连检测定时器关闭");
_Disconnect(true);
+
+ Save();
}
public override void Clear()
diff --git a/QuantBox.API.Provider/Single/SingleProvider.Settings.cs b/QuantBox.APIProvider/Single/SingleProvider.Settings.cs
similarity index 81%
rename from QuantBox.API.Provider/Single/SingleProvider.Settings.cs
rename to QuantBox.APIProvider/Single/SingleProvider.Settings.cs
index 21cb19e..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";
@@ -22,10 +19,12 @@ public partial class SingleProvider : Provider
private const string CATEGORY_QUOTE_REQUEST = "Settings - QuoteRequest";
private const string CATEGORY_INSTRUMENT = "Settings - Instrument";
private const string CATEGORY_HISTORICAL_DATA = "Settings - HistoricalData";
-
+ private const string CATEGORY_LOG_INFO = "Settings - Log Info";
+
private bool _enableEmitData;
private bool _emitBidAsk;
private bool _emitBidAskFirst;
+ private bool _emitLevel2Snapshot;
#region 行情配置
@@ -54,6 +53,13 @@ public bool EmitBidAskFirst
get { return _emitBidAskFirst; }
set { _emitBidAskFirst = value; }
}
+ [Category(CATEGORY_MARKETDATA)]
+ [Description("【行情】触发OnLevel2Snapshot事件")]
+ public bool EmitLevel2Snapshot
+ {
+ get { return _emitLevel2Snapshot; }
+ set { _emitLevel2Snapshot = value; }
+ }
#endregion
@@ -121,31 +127,31 @@ 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 { return _configPath; }
- set
- {
- _configPath = value;
- Load();
- }
- }
- private string _configPath;
+ public string ConfigPath { get; set; }
[Category(CATEGORY_COMMON)]
[Description("交易时段列表,当前时间在这些列表中将启用重连机制,不在此列表中将主动断开,列表为空将不处理")]
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,11 +179,21 @@ public string ConfigPath
[Category(CATEGORY_HISTORICAL_DATA)]
[Description("【历史】是否触发EmitHistoricalData事件")]
[DisplayName("EmitHistoricalData")]
- public bool EnableEmitHistoricalData { get; set; }
+ public bool EnablEmitHistoricalData { get; set; }
[Category(CATEGORY_HISTORICAL_DATA)]
[Description("【历史】是否过滤数据日期和时间")]
public bool FilterDateTime { get; set; }
#endregion
+
+ [Category(CATEGORY_LOG_INFO)]
+ [Description("【日志】是否显示OnRspQryInvestorPosition记录")]
+ public bool IsLogOnRspQryInvestorPosition { get; set; }
+ [Category(CATEGORY_LOG_INFO)]
+ [Description("【日志】是否显示OnRspQryTradingAccount记录")]
+ public bool IsLogOnRspQryTradingAccount { get; set; }
+ [Category(CATEGORY_LOG_INFO)]
+ [Description("【日志】是否显示OnRtnInstrumentStatus记录")]
+ public bool IsLogOnRtnInstrumentStatus { get; set; }
}
}
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 78%
rename from QuantBox.API.Provider/UI/ApiControlForm.cs
rename to QuantBox.APIProvider/UI/ApiControlForm.cs
index 28b6118..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()
@@ -27,46 +22,46 @@ public void Init(SingleProvider provider)
private void button_QueryOrder_Click(object sender, EventArgs e)
{
- ReqQueryField query = default(ReqQueryField);
+ ReqQueryField query = new ReqQueryField();
query.PortfolioID1 = textBox_PortfolioID1.Text;
query.PortfolioID2 = textBox_PortfolioID2.Text;
query.PortfolioID3 = textBox_PortfolioID3.Text;
query.Business = (BusinessType)Enum.Parse(typeof(BusinessType),comboBox_BusinessType.Text);
- provider._QueryApi.ReqQuery(QueryType.ReqQryOrder,ref query);
+ provider._QueryApi.ReqQuery(QueryType.ReqQryOrder, query);
}
private void button_QueryTrade_Click(object sender, EventArgs e)
{
- ReqQueryField query = default(ReqQueryField);
+ ReqQueryField query = new ReqQueryField();
query.PortfolioID1 = textBox_PortfolioID1.Text;
query.PortfolioID2 = textBox_PortfolioID2.Text;
query.PortfolioID3 = textBox_PortfolioID3.Text;
query.Business = (BusinessType)Enum.Parse(typeof(BusinessType), comboBox_BusinessType.Text);
- provider._QueryApi.ReqQuery(QueryType.ReqQryTrade, ref query);
+ provider._QueryApi.ReqQuery(QueryType.ReqQryTrade, query);
}
private void button_QueryAccount_Click(object sender, EventArgs e)
{
- ReqQueryField query = default(ReqQueryField);
+ ReqQueryField query = new ReqQueryField();
query.PortfolioID1 = textBox_PortfolioID1.Text;
query.PortfolioID2 = textBox_PortfolioID2.Text;
query.PortfolioID3 = textBox_PortfolioID3.Text;
query.Business = (BusinessType)Enum.Parse(typeof(BusinessType), comboBox_BusinessType.Text);
- provider._QueryApi.ReqQuery(QueryType.ReqQryTradingAccount, ref query);
+ provider._QueryApi.ReqQuery(QueryType.ReqQryTradingAccount, query);
}
private void button_QueryPosition_Click(object sender, EventArgs e)
{
- ReqQueryField query = default(ReqQueryField);
+ ReqQueryField query = new ReqQueryField();
query.PortfolioID1 = textBox_PortfolioID1.Text;
query.PortfolioID2 = textBox_PortfolioID2.Text;
query.PortfolioID3 = textBox_PortfolioID3.Text;
query.Business = (BusinessType)Enum.Parse(typeof(BusinessType), comboBox_BusinessType.Text);
- provider._QueryApi.ReqQuery(QueryType.ReqQryInvestorPosition, ref query);
+ provider._QueryApi.ReqQuery(QueryType.ReqQryInvestorPosition, query);
}
private void ApiControlForm_Load(object sender, EventArgs e)
@@ -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 83%
rename from QuantBox.API.Provider/UI/ApiManagerForm.Designer.cs
rename to QuantBox.APIProvider/UI/ApiManagerForm.Designer.cs
index 69267ed..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
@@ -61,9 +62,11 @@ private void InitializeComponent()
this.listBox_UserList.DataSource = this.userItemBindingSource;
this.listBox_UserList.FormattingEnabled = true;
this.listBox_UserList.HorizontalScrollbar = true;
- this.listBox_UserList.Location = new System.Drawing.Point(14, 19);
+ this.listBox_UserList.ItemHeight = 15;
+ this.listBox_UserList.Location = new System.Drawing.Point(19, 22);
+ this.listBox_UserList.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.listBox_UserList.Name = "listBox_UserList";
- this.listBox_UserList.Size = new System.Drawing.Size(313, 108);
+ this.listBox_UserList.Size = new System.Drawing.Size(416, 124);
this.listBox_UserList.TabIndex = 0;
this.listBox_UserList.SelectedIndexChanged += new System.EventHandler(this.listBox_UserList_SelectedIndexChanged);
//
@@ -76,9 +79,10 @@ private void InitializeComponent()
this.propertyGrid.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
- this.propertyGrid.Location = new System.Drawing.Point(439, -1);
+ this.propertyGrid.Location = new System.Drawing.Point(585, -1);
+ this.propertyGrid.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.propertyGrid.Name = "propertyGrid";
- this.propertyGrid.Size = new System.Drawing.Size(435, 468);
+ this.propertyGrid.Size = new System.Drawing.Size(580, 540);
this.propertyGrid.TabIndex = 1;
this.propertyGrid.PropertyValueChanged += new System.Windows.Forms.PropertyValueChangedEventHandler(this.propertyGrid_PropertyValueChanged);
//
@@ -88,18 +92,21 @@ private void InitializeComponent()
this.groupBox1.Controls.Add(this.button_RemoveUser);
this.groupBox1.Controls.Add(this.button_AddUser);
this.groupBox1.Controls.Add(this.listBox_UserList);
- this.groupBox1.Location = new System.Drawing.Point(12, 12);
+ this.groupBox1.Location = new System.Drawing.Point(16, 14);
+ this.groupBox1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.groupBox1.Name = "groupBox1";
- this.groupBox1.Size = new System.Drawing.Size(421, 142);
+ this.groupBox1.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3);
+ this.groupBox1.Size = new System.Drawing.Size(561, 164);
this.groupBox1.TabIndex = 2;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "UserList";
//
// button_CopyUser
//
- this.button_CopyUser.Location = new System.Drawing.Point(333, 77);
+ this.button_CopyUser.Location = new System.Drawing.Point(444, 89);
+ this.button_CopyUser.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.button_CopyUser.Name = "button_CopyUser";
- this.button_CopyUser.Size = new System.Drawing.Size(75, 23);
+ this.button_CopyUser.Size = new System.Drawing.Size(100, 27);
this.button_CopyUser.TabIndex = 1;
this.button_CopyUser.Text = "Copy";
this.button_CopyUser.UseVisualStyleBackColor = true;
@@ -107,9 +114,10 @@ private void InitializeComponent()
//
// button_RemoveUser
//
- this.button_RemoveUser.Location = new System.Drawing.Point(333, 48);
+ this.button_RemoveUser.Location = new System.Drawing.Point(444, 55);
+ this.button_RemoveUser.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.button_RemoveUser.Name = "button_RemoveUser";
- this.button_RemoveUser.Size = new System.Drawing.Size(75, 23);
+ this.button_RemoveUser.Size = new System.Drawing.Size(100, 27);
this.button_RemoveUser.TabIndex = 1;
this.button_RemoveUser.Text = "Remove";
this.button_RemoveUser.UseVisualStyleBackColor = true;
@@ -117,9 +125,10 @@ private void InitializeComponent()
//
// button_AddUser
//
- this.button_AddUser.Location = new System.Drawing.Point(333, 19);
+ this.button_AddUser.Location = new System.Drawing.Point(444, 22);
+ this.button_AddUser.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.button_AddUser.Name = "button_AddUser";
- this.button_AddUser.Size = new System.Drawing.Size(75, 23);
+ this.button_AddUser.Size = new System.Drawing.Size(100, 27);
this.button_AddUser.TabIndex = 1;
this.button_AddUser.Text = "Add";
this.button_AddUser.UseVisualStyleBackColor = true;
@@ -131,18 +140,21 @@ private void InitializeComponent()
this.groupBox2.Controls.Add(this.button_RemoveServer);
this.groupBox2.Controls.Add(this.listBox_ServerList);
this.groupBox2.Controls.Add(this.button_AddServer);
- this.groupBox2.Location = new System.Drawing.Point(12, 160);
+ this.groupBox2.Location = new System.Drawing.Point(16, 185);
+ this.groupBox2.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.groupBox2.Name = "groupBox2";
- this.groupBox2.Size = new System.Drawing.Size(421, 142);
+ this.groupBox2.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3);
+ this.groupBox2.Size = new System.Drawing.Size(561, 164);
this.groupBox2.TabIndex = 2;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "ServerList";
//
// button_CopyServer
//
- this.button_CopyServer.Location = new System.Drawing.Point(333, 80);
+ this.button_CopyServer.Location = new System.Drawing.Point(444, 92);
+ this.button_CopyServer.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.button_CopyServer.Name = "button_CopyServer";
- this.button_CopyServer.Size = new System.Drawing.Size(75, 23);
+ this.button_CopyServer.Size = new System.Drawing.Size(100, 27);
this.button_CopyServer.TabIndex = 1;
this.button_CopyServer.Text = "Copy";
this.button_CopyServer.UseVisualStyleBackColor = true;
@@ -150,9 +162,10 @@ private void InitializeComponent()
//
// button_RemoveServer
//
- this.button_RemoveServer.Location = new System.Drawing.Point(333, 51);
+ this.button_RemoveServer.Location = new System.Drawing.Point(444, 59);
+ this.button_RemoveServer.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.button_RemoveServer.Name = "button_RemoveServer";
- this.button_RemoveServer.Size = new System.Drawing.Size(75, 23);
+ this.button_RemoveServer.Size = new System.Drawing.Size(100, 27);
this.button_RemoveServer.TabIndex = 1;
this.button_RemoveServer.Text = "Remove";
this.button_RemoveServer.UseVisualStyleBackColor = true;
@@ -163,17 +176,20 @@ private void InitializeComponent()
this.listBox_ServerList.DataSource = this.serverItemBindingSource;
this.listBox_ServerList.FormattingEnabled = true;
this.listBox_ServerList.HorizontalScrollbar = true;
- this.listBox_ServerList.Location = new System.Drawing.Point(14, 19);
+ this.listBox_ServerList.ItemHeight = 15;
+ this.listBox_ServerList.Location = new System.Drawing.Point(19, 22);
+ this.listBox_ServerList.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.listBox_ServerList.Name = "listBox_ServerList";
- this.listBox_ServerList.Size = new System.Drawing.Size(313, 108);
+ this.listBox_ServerList.Size = new System.Drawing.Size(416, 124);
this.listBox_ServerList.TabIndex = 0;
this.listBox_ServerList.SelectedIndexChanged += new System.EventHandler(this.listBox_ServerList_SelectedIndexChanged);
//
// button_AddServer
//
- this.button_AddServer.Location = new System.Drawing.Point(333, 22);
+ this.button_AddServer.Location = new System.Drawing.Point(444, 25);
+ this.button_AddServer.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.button_AddServer.Name = "button_AddServer";
- this.button_AddServer.Size = new System.Drawing.Size(75, 23);
+ this.button_AddServer.Size = new System.Drawing.Size(100, 27);
this.button_AddServer.TabIndex = 1;
this.button_AddServer.Text = "Add";
this.button_AddServer.UseVisualStyleBackColor = true;
@@ -187,18 +203,21 @@ private void InitializeComponent()
this.groupBox3.Controls.Add(this.button_RemoveApi);
this.groupBox3.Controls.Add(this.listBox_ApiList);
this.groupBox3.Controls.Add(this.button_AddApi);
- this.groupBox3.Location = new System.Drawing.Point(12, 314);
+ this.groupBox3.Location = new System.Drawing.Point(16, 362);
+ this.groupBox3.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.groupBox3.Name = "groupBox3";
- this.groupBox3.Size = new System.Drawing.Size(421, 142);
+ this.groupBox3.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3);
+ this.groupBox3.Size = new System.Drawing.Size(561, 164);
this.groupBox3.TabIndex = 2;
this.groupBox3.TabStop = false;
this.groupBox3.Text = "ApiList";
//
// button_CopyApi
//
- this.button_CopyApi.Location = new System.Drawing.Point(333, 76);
+ this.button_CopyApi.Location = new System.Drawing.Point(444, 88);
+ this.button_CopyApi.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.button_CopyApi.Name = "button_CopyApi";
- this.button_CopyApi.Size = new System.Drawing.Size(75, 23);
+ this.button_CopyApi.Size = new System.Drawing.Size(100, 27);
this.button_CopyApi.TabIndex = 1;
this.button_CopyApi.Text = "Copy";
this.button_CopyApi.UseVisualStyleBackColor = true;
@@ -206,9 +225,10 @@ private void InitializeComponent()
//
// button_RemoveApi
//
- this.button_RemoveApi.Location = new System.Drawing.Point(333, 47);
+ this.button_RemoveApi.Location = new System.Drawing.Point(444, 54);
+ this.button_RemoveApi.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.button_RemoveApi.Name = "button_RemoveApi";
- this.button_RemoveApi.Size = new System.Drawing.Size(75, 23);
+ this.button_RemoveApi.Size = new System.Drawing.Size(100, 27);
this.button_RemoveApi.TabIndex = 1;
this.button_RemoveApi.Text = "Remove";
this.button_RemoveApi.UseVisualStyleBackColor = true;
@@ -222,9 +242,11 @@ private void InitializeComponent()
this.listBox_ApiList.DataSource = this.apiItemBindingSource;
this.listBox_ApiList.FormattingEnabled = true;
this.listBox_ApiList.HorizontalScrollbar = true;
- this.listBox_ApiList.Location = new System.Drawing.Point(14, 19);
+ this.listBox_ApiList.ItemHeight = 15;
+ this.listBox_ApiList.Location = new System.Drawing.Point(19, 22);
+ this.listBox_ApiList.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.listBox_ApiList.Name = "listBox_ApiList";
- this.listBox_ApiList.Size = new System.Drawing.Size(313, 108);
+ this.listBox_ApiList.Size = new System.Drawing.Size(416, 124);
this.listBox_ApiList.TabIndex = 0;
this.listBox_ApiList.SelectedIndexChanged += new System.EventHandler(this.listBox_ApiList_SelectedIndexChanged);
//
@@ -234,9 +256,10 @@ private void InitializeComponent()
//
// button_AddApi
//
- this.button_AddApi.Location = new System.Drawing.Point(333, 18);
+ this.button_AddApi.Location = new System.Drawing.Point(444, 21);
+ this.button_AddApi.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.button_AddApi.Name = "button_AddApi";
- this.button_AddApi.Size = new System.Drawing.Size(75, 23);
+ this.button_AddApi.Size = new System.Drawing.Size(100, 27);
this.button_AddApi.TabIndex = 1;
this.button_AddApi.Text = "Add";
this.button_AddApi.UseVisualStyleBackColor = true;
@@ -244,15 +267,17 @@ private void InitializeComponent()
//
// ApiManagerForm
//
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.ClientSize = new System.Drawing.Size(873, 468);
+ this.ClientSize = new System.Drawing.Size(1164, 540);
this.Controls.Add(this.groupBox3);
this.Controls.Add(this.groupBox2);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.propertyGrid);
+ this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.Name = "ApiManagerForm";
this.Text = "ApiManagerForm";
+ this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.ApiManagerForm_FormClosed);
this.Load += new System.EventHandler(this.ApiManagerForm_Load);
((System.ComponentModel.ISupportInitialize)(this.userItemBindingSource)).EndInit();
this.groupBox1.ResumeLayout(false);
@@ -264,7 +289,7 @@ private void InitializeComponent()
}
- #endregion
+#endregion
private System.Windows.Forms.ListBox listBox_UserList;
private System.Windows.Forms.PropertyGrid propertyGrid;
@@ -286,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 96%
rename from QuantBox.API.Provider/UI/ApiManagerForm.cs
rename to QuantBox.APIProvider/UI/ApiManagerForm.cs
index 14dd317..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()
@@ -154,6 +156,7 @@ private void listBox_ApiList_SelectedIndexChanged(object sender, EventArgs e)
public void Init(SingleProvider provider)
{
this.provider = provider;
+ provider.Load();
}
private void ApiManagerForm_Load(object sender, EventArgs e)
@@ -162,5 +165,11 @@ private void ApiManagerForm_Load(object sender, EventArgs e)
serverItemBindingSource.DataSource = provider.ServerList;
apiItemBindingSource.DataSource = provider.ApiList;
}
+
+ 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 89%
rename from QuantBox.API.Provider/UI/ApiTypeSelectorEditor.cs
rename to QuantBox.APIProvider/UI/ApiTypeSelectorEditor.cs
index 024f5c8..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;
@@ -42,9 +41,9 @@ protected override void FillTreeWithData(ObjectSelectorEditor.Selector selector,
selector.Clear();
foreach (ApiType category in Enum.GetValues(typeof(ApiType)))
{
- if (category != ApiType.Nono)
+ 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/OrderExtensions_OpenCloseType.cs b/QuantBox.Extensions/OrderExtensions_OpenCloseType.cs
index bcad954..b356826 100644
--- a/QuantBox.Extensions/OrderExtensions_OpenCloseType.cs
+++ b/QuantBox.Extensions/OrderExtensions_OpenCloseType.cs
@@ -32,6 +32,7 @@ public static Order CloseToday(this Order order)
public static Order SetOpenClose(this Order order, OpenCloseType OpenClose)
{
+ order.SubSide = order.OpenClose2SubSide(OpenClose);
order.GetDictionary(index)[OrderTagType.PositionEffect] = (byte)OpenClose;
return order;
}
@@ -39,11 +40,53 @@ public static Order SetOpenClose(this Order order, OpenCloseType OpenClose)
public static OpenCloseType? GetOpenClose(this Order order)
{
object obj = order.GetDictionaryValue(OrderTagType.PositionEffect, index);
- if(obj == null)
+ if (obj == null)
{
return (OpenCloseType?)obj;
}
return (OpenCloseType?)(byte)obj;
}
- }
+
+ public static SubSide OpenClose2SubSide(this Order order, OpenCloseType OpenClose)
+ {
+ //多头
+ //Buy 就是开
+ //Sell 就是平 SubSide 是 Undefined
+
+ //空头
+ //Sell 加 SubSide = SellShort 是开仓
+ //Buy 加 SubSide = BuyCover 是平仓
+
+ // 由于使用官方的办法无法指定平今与平昨,所以还是用以前的开平仓的写法
+ // 区别只是官方维护了双向持仓
+ if (order.Side == SmartQuant.OrderSide.Buy)
+ {
+ switch (OpenClose)
+ {
+ case OpenCloseType.Open:
+ order.SubSide = SubSide.Undefined;
+ break;
+ case OpenCloseType.Close:
+ case OpenCloseType.CloseToday:
+ order.SubSide = SubSide.BuyCover;
+ break;
+ }
+ }
+ else
+ {
+ switch (OpenClose)
+ {
+ case OpenCloseType.Open:
+ order.SubSide = SubSide.SellShort;
+ break;
+ case OpenCloseType.Close:
+ case OpenCloseType.CloseToday:
+ order.SubSide = SubSide.Undefined;
+ break;
+ }
+ }
+
+ return order.SubSide;
+ }
+ }
}
diff --git a/QuantBox.Extensions/OrderTagType.cs b/QuantBox.Extensions/OrderTagType.cs
index e7948eb..a6d852d 100644
--- a/QuantBox.Extensions/OrderTagType.cs
+++ b/QuantBox.Extensions/OrderTagType.cs
@@ -16,7 +16,7 @@ public class OrderTagType
// 在本地就可以处理好的数据类型,没有必要在网络中传输,并且有可能无法序列化
public const int Local = 1;
// 留给用户自定义的数据类型,让用户自己发挥的类型,可做临时变量等等
- public const int Custom = 2;
+ public static int Index_MAX = 2;
// ===== 需要跨网络传输的Tag
@@ -30,6 +30,7 @@ public class OrderTagType
public const int PortfolioID3 = 6;
public const int Business = 7;
public const int QuoteReqID = 8; // 这个名字可能有错,需要确认
+ public static int Network_MAX = 9;
// ===== 不需要网络传输的类型,并且有可能无法序列化
@@ -37,5 +38,23 @@ public class OrderTagType
public const int SameTimeOrder = 1; // 特殊的用于记录关联的Order,但没有先后循序,如Quote报单
public const int CancelCount = 2; // 定时撤单次数,用来区分是否跟单功能撤单
public const int SendCount = 3;//记录跟单时重发次数
+ public static int Local_MAX = 4;
}
}
+
+// 使用static 定义,主要是想在不同的模块下,ID不冲突
+// 目前这个写法还没有在不同模块下测试过
+
+//public class MyOrderTagType
+//{
+// // TODO: 这种方法是否有坑
+// public static int TargetAmount = OrderTagType.Local_MAX + 1;
+// public static int MaxQtyPerLot = TargetAmount + 1;
+// public static int TimesTicks = MaxQtyPerLot + 1;
+// public static int MaxCancelCnt = TimesTicks + 1;
+
+// static MyOrderTagType()
+// {
+// OrderTagType.Local_MAX = MaxCancelCnt + 1;
+// }
+//}
diff --git a/QuantBox.Extensions/QuantBox.Extensions.csproj b/QuantBox.Extensions/QuantBox.Extensions.csproj
index bd56cfe..1283ef8 100644
--- a/QuantBox.Extensions/QuantBox.Extensions.csproj
+++ b/QuantBox.Extensions/QuantBox.Extensions.csproj
@@ -1,80 +1,17 @@
-
-
-
+
+
- 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
- C:\Program Files\SmartQuant Ltd\OpenQuant 2014\
- TRACE
- prompt
- 4
-
-
-
+ netstandard2.0
+ false
+
-
- False
+
C:\Program Files\SmartQuant Ltd\OpenQuant 2014\SmartQuant.dll
- False
-
-
-
-
-
-
- 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