C# 主观交易之 超强T+0 实盘辅助交易工具

经过连续一个月的封装斗争,这个T+0的交易工具终于完成了。

交易工具针对团队的手工T+0交易,能非常个性化的辅助交易、完美实现报撤单和查看实时持仓的需求。

一、从技术男的角度来解读这个交易工具的核心技术部分:

工具大量使用了win底层的Api;

1)、鼠标全局钩子:

使用鼠标全局钩子捕捉鼠标的动作,使用到了以下图的Api函数

2)、捕捉全局鼠标动作:

 HOOKPROC hProc = new AllWinApi.HOOKPROC(MouseHookProc);

int hHook = AllWinApi.SetWindowsHookEx(EnumHookType.WM_MOUSE_LL, hProc, IntPtr.Zero, 0);

注册了全局钩子以后,使用Api的回调函数开始捕捉鼠标的单击行为

 

 

 

3)、winform调用

实现了这个钩子的封装以后,直接在需要调用的窗体启动时实例化钩子(窗体dispose的时候卸载钩子),如下操作:

4)、注册/卸载快捷键(HotKey)

注册快捷键这一步对工具实现业务逻辑是非常重要的,因为在不同的场景下需要反复使用,所以这里将其封装起来

二、从技术男的角度来解读这个交易工具的核心业务部分:

1)、快捷键enter实现报单:

                case 105:
                            if (hft.ThisForm == null) return;
                            var myPanel = GetT<Panel>(hft.ThisForm, "FormPanelName");
                            //var myPanel = GetPanel(hft.ThisForm, "FormPanelName");
                            var cName5 = hft.ThisForm.Text;
                            if (!CodeNameFFFFdict.ContainsKey(cName5))
                            {
                                MessageBox.Show($"找不到该合约对应的窗体,请重新操作...");
                                return;
                            }
                            var allFFFF5 = CodeNameFFFFdict[cName5];
                            var TF1 = allFFFF5.checkPanelTimes.F1;
                            var TF2 = allFFFF5.checkPanelTimes.F2;
                            var TF3 = allFFFF5.checkPanelTimes.F3;
                            var TF4 = allFFFF5.checkPanelTimes.F4;
                            if (tradeField.code != null && myPanel != null && (TF1 > 0 || TF2 > 0 || TF3 > 0 || TF4 > 0)){
                                try{
                                    tuple_ = GetPanelTradeData(myPanel);
                                    OrderInsertInfoDataAny orderInsert = new OrderInsertInfoDataAny{
                                        InstrumentID = tradeField.code,
                                        Direction = tradeField.directionTypeAny,
                                        CombOffsetFlag = tradeField.offsetFlagTypeAny,
                                        ExchangeID = tradeField.exchangeIDTypeAny,
                                        VolumeTotalOriginal = tuple_.Item1,
                                        LimitPrice = tuple_.Item2
                                    };
                                    var orderR = OnOrderInsertEvent?.Invoke(orderInsert);
                                    myPanel.Visible = false;  //隐藏panel控件
                                    allFFFF5.checkPanelTimes = new CheckPanelTimes();
                                    CodeNameFFFFdict[cName5] = allFFFF5;
                                    ten.Log($"发出一笔委托订单:{orderR}");
                                }
                                catch (Exception e){
                                    ten.Log($"推送订单失败,error:{e.Message}");
                                }

        /// <summary>
        /// 接收委托订单信息
        /// </summary>
        /// <param name="obj"></param>
        private string Form_OnOrderInsertEvent(OrderInsertInfoDataAny obj)
        {
            return TradeApi.OrderInsert(obj.InstrumentID, obj.ExchangeID, obj.Direction, obj.CombOffsetFlag, obj.LimitPrice, obj.VolumeTotalOriginal);
        }

2)、收集所有委托回报、维护未成交队列

         /// <summary>
        /// 记录所有未成交的RtnOrder
        /// 1.从OnRtnOrder开始记录
        /// 2.一旦成交(全部、部分),则修改队列中的数据(全部成交则提出队列,部分成交则修改未成交数量)
        /// </summary>
        public Dictionary<string, OrderReturnInforDataAny> NotTradeQueeingDict;
        private void TradeApi_OnRtnOrder(IBaseTdApi arg1, OrderReturnInforDataAny arg2)
        {
            var traded = EnumOrderStatusTypeAny.AllTraded;
            var v_orinal = arg2.VolumeTotalOriginal;
            var status = arg2.OrderStatus;
            var msg = arg2.OrderStatusMsg;
            if (msg == "未成交" || msg == "部分成交")
                NotTradeQueeingDict[arg2.OrderRef] = arg2;
            else if (status == traded || v_orinal == arg2.VolumeTraded || msg == "已撤单")
            {
                if (msg == "全部成交")
                    AllTradedQueeingDict[arg2.OrderRef] = arg2;
                NotTradeQueeingDict.Remove(arg2.OrderRef);
            }
            if (arg2.OrderStatusMsg.Contains("拒绝"))
                Log($"{arg2.Code}:OrderRef为{arg2.OrderRef}==>{arg2.OrderStatusMsg}");
            if(NotTradeQueeingDict.Count()>=1||(NotTradeQueeingDict.Count()==0&& msg == "已撤单")
                ||(NotTradeQueeingDict.Count() == 0 && msg == "全部成交"))
            OnNotTradeQueeing?.Invoke(NotTradeQueeingDict);
        }

 

         /// <summary>
        /// 期指和股票撤单
        /// </summary>
        /// <param name="arg"></param>
        public void GetCancle(OrderReturnInforDataAny arg)
        {
            lock (o)
            {
                try
                {
                    //实盘撤单逻辑
                    if (NotTradeQueeingDict.Keys.Contains(arg.OrderRef))
                        this.TradeApi.OrderAction(arg.Code, arg.OrderSysID, arg.SessionID, arg.FrontID, arg.OrderRef);
                    else
                        this.TradeApi.OrderAction(arg.Code, arg.OrderSysID, arg.SessionID, arg.FrontID, arg.OrderRef);
                    
                }
                catch (Exception e)
                { this.Log($"撤单报错:{e.Message}" ); }
            }
        }

3)、获取实时账户持仓信息

        /// <summary>
        /// 持仓明细
        /// </summary>
        /// <param name="arg1"></param>
        /// <param name="arg2"></param>
        /// <param name="arg3"></param>
        /// <param name="arg4"></param>
        /// <param name="arg5"></param>
        private void TradeApi_OnRspQryInvestorDetailPosition(IBaseTdApi arg1, OnRspInvestorPositonDelailAny arg2, ErrorInfoAny arg3, int arg4, bool arg5)
        {
            OnPositionDelailField?.Invoke(arg2);
        }

4)、确保行情账户持续在线(断线重连)

private void QuotaApiStock_deep_QuoteApiDisConnected(IBaseMdApi arg1, int arg2)
        {
            //API断线重连

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

三、工具最后成型的可视化展示:

1)、主界面

2)、委托界面

3)、交易面板

4)、未成交、实时持仓

 

5)、结案

QQ:1013359736

想进一步了解的朋友们,可以加我QQ,^_^

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值