美文网首页
技术图文:如何进行代码的重构?以封装 BigOne API 为例

技术图文:如何进行代码的重构?以封装 BigOne API 为例

作者: 老马的程序人生 | 来源:发表于2019-06-28 08:53 被阅读0次

    背景

    自从把“量化交易”作为自己精进的技术方向之后,我做了一些准备工作。

    比如:

    <u>1. 爬取交易所的公告,根据公告的信息来研判数字货币的短期走势</u>。

    这里面有一个“流动性溢价”的概念,等后面我会结合一些例子跟大家聊聊这块的发现。

    <u>2. 爬取平台币 One 的数据,根据数据来估计该数字货币的价值</u>。

    <u>3. 封装 BigOne 交易所的 API,改进“网格交易法”做了一款自动化交易系统</u>。


    技术分析

    写程序的码农们所追求的一定是自己写的代码是 <u>可复用、可扩展、易维护、灵活性好</u> 的。所以,这两天我用 Layers软件体系结构风格 对自己的交易系统进行了重构。

    什么是 Layers软件体系结构风格 呢?

    层次系统组织成一个层次结构,每一层为上一层提供服务,并作为下一层的客户。

    Layers软件体系结构风格

    这种风格的优点是:

    • 支持基于可增加抽象的层的设计。这样允许将一个复杂问题分解成一个增量步骤序列的实现。
    • 支持系统改进。和管道过滤器风格一样,因为每一层最多只与上下相邻两层交互,所以改变每一层的功能最多只影响两层。
    • 支持软件复用。和抽象数据类型风格一样,只要给相邻层提供相同的接口,允许每一层用不同的方法实现。这使得在标准的接口上构建不同的实现成为可能。

    基于这种风格,我把 BigOne API 封装为三层:

    • 通讯层,该层的作用是:封装 GetSet 请求,与 BigOne 服务器进行通讯,为中间层提供支撑。
    • 中间层,该层的作用是:调用通讯层,获取 BigOne 服务器返回的数据,该数据不进行处理。
    • 应用层,该层的作用是:为客户端提供对象群体,通过对象之间的协作辅助客户端实现用户需求。

    代码实现

    Step1 封装最底层 -- 通讯层

    internal class HttpUtilManagerBigOne
    {
        
        private const string ApiKey = "";//您的API Key
        private const string ApiSecret = "";//您的API Secret
        private static readonly HttpUtilManagerBigOne Instance = new HttpUtilManagerBigOne();//单例模式
    
        private HttpUtilManagerBigOne()
        {
    
        }
    
        public static HttpUtilManagerBigOne GetInstance()
        {
            return Instance;
        }
    
        public string GetToken()
        {
            DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
    
            long timestamp = Convert.ToInt64(
                ((long) (DateTime.Now - startTime).TotalMilliseconds).ToString(CultureInfo.InvariantCulture)
                    .PadRight(19, '0'));
    
            IDictionary<string, object> payload = new Dictionary<string, object>
            {
                {"type", "OpenAPI"},
                {"sub", ApiKey},
                {"nonce", timestamp}
            };
            byte[] secretKey = Encoding.Default.GetBytes(ApiSecret);
            string token = JWT.Encode(payload, secretKey, JwsAlgorithm.HS256);
            return token;
        }
    
        public string RequestHttpGet(string url, string param, bool withToken = false)
        {
            if (string.IsNullOrEmpty(url))
                throw new ArgumentNullException();
    
            if (string.IsNullOrEmpty(param)==false)
            {
                if (url.EndsWith("?"))
                {
                    url = url + param;
                }
                else
                {
                    url = url + "?" + param;
                }
            }
    
            HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
            if (request == null)
                return string.Empty;
    
            request.ProtocolVersion = HttpVersion.Version10;
            request.Method = "GET";
            request.Timeout = 30000;
            if (withToken)
            {
                string token = "Bearer " + GetToken();
                request.Headers.Add("authorization", token);
            }
    
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            ServicePointManager.Expect100Continue = true; 
            try
            {
                HttpWebResponse response = (HttpWebResponse) request.GetResponse();
                Stream stream = response.GetResponseStream();
                if (stream == null)
                    return string.Empty;
    
                StreamReader reader = new StreamReader(stream, Encoding.UTF8);
                return reader.ReadToEnd();
            }
            catch
            {
                return string.Empty;
            }
        }
    
        public string RequestHttpPost(string url, Dictionary<string, string> arguments)
        {
            int i = 0;
            StringBuilder builder = new StringBuilder();
            foreach (KeyValuePair<string, string> item in arguments)
            {
                if (i > 0)
                {
                    builder.Append("&");
                }
                builder.AppendFormat("{0}={1}", item.Key, item.Value);
                i++;
            }
    
            HttpWebRequest request = (HttpWebRequest) WebRequest.Create(url);
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
    
            string token = "Bearer " + GetToken();
            request.Headers.Add("authorization", token);
    
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            ServicePointManager.Expect100Continue = true; 
    
            byte[] data = Encoding.UTF8.GetBytes(builder.ToString());
            request.ContentLength = data.Length;
            using (Stream reqStream = request.GetRequestStream())
            {
                reqStream.Write(data, 0, data.Length);
                reqStream.Close();
            }
            try
            {
                HttpWebResponse resp = (HttpWebResponse) request.GetResponse();
                Stream stream = resp.GetResponseStream();
    
                if (stream == null)
                    return string.Empty;
    
                StreamReader streamReader = new StreamReader(stream, Encoding.UTF8);
                return streamReader.ReadToEnd();
            }
            catch
            {
                return string.Empty;
            }
        }
    }
    

    Step2 封装中间层

    internal class RestApiBigOne
    {
        public string ApiEntryPoint = @"https://big.one/api/v2/";
        public string GetPing()
        {
            HttpUtilManagerBigOne httpUtil = HttpUtilManagerBigOne.GetInstance();
            string url = ApiEntryPoint + "ping";
            string param = "";
            string result = httpUtil.RequestHttpGet(url, param);
            return result;
        }
        
        public string GetTickers()
        {
            HttpUtilManagerBigOne httpUtil = HttpUtilManagerBigOne.GetInstance();
            string url = ApiEntryPoint + "tickers";
            string param = "";
            string result = httpUtil.RequestHttpGet(url, param);
            return result;
        }
        
        public string GetTicker(string marketId)
        {
            HttpUtilManagerBigOne httpUtil = HttpUtilManagerBigOne.GetInstance();
            string url = ApiEntryPoint + "markets/" + marketId + "/ticker";
            string param = "market_id=" + marketId;
            string result = httpUtil.RequestHttpGet(url, param);
            return result;
        }
    
        public string GetOrderBook(string marketId)
        {
            HttpUtilManagerBigOne httpUtil = HttpUtilManagerBigOne.GetInstance();
            string url = ApiEntryPoint + "markets/" + marketId + "/depth";
            string param = "market_id=" + marketId;
            string result = httpUtil.RequestHttpGet(url, param);
            return result;
        }
        
        public string GetMarketTrade(string marketId)
        {
            HttpUtilManagerBigOne httpUtil = HttpUtilManagerBigOne.GetInstance();
            string url = ApiEntryPoint + "markets/" + marketId + "/trades";
            string param = "market_id=" + marketId;
            string result = httpUtil.RequestHttpGet(url, param);
            return result;
        }
        
        public string GetMarket()
        {
            HttpUtilManagerBigOne httpUtil = HttpUtilManagerBigOne.GetInstance();
            string url = ApiEntryPoint + "markets";
            string param = "";
            string result = httpUtil.RequestHttpGet(url, param);
            return result;
        }
        
        public string GetAccount()
        {
            HttpUtilManagerBigOne httpUtil = HttpUtilManagerBigOne.GetInstance();
            string url = ApiEntryPoint + "viewer/accounts";
            string param = "";
            string result = httpUtil.RequestHttpGet(url, param, true);
            return result;
        }
        
        public string GetOrders()
        {
            HttpUtilManagerBigOne httpUtil = HttpUtilManagerBigOne.GetInstance();
            string url = ApiEntryPoint + "viewer/orders";
            string param = "";
            string result = httpUtil.RequestHttpGet(url, param, true);
            return result;
        }
        
        public string GetOrder(string orderId)
        {
            HttpUtilManagerBigOne httpUtil = HttpUtilManagerBigOne.GetInstance();
            string url = ApiEntryPoint + "viewer/orders/" + orderId;
            string param = "order_id=" + orderId;
            string result = httpUtil.RequestHttpGet(url, param, true);
            return result;
        }
        
        public string CreateOrder(string marketId, string side, string price, string amount)
        {
            HttpUtilManagerBigOne httpUtil = HttpUtilManagerBigOne.GetInstance();
            string url = ApiEntryPoint + "viewer/orders";
            Dictionary<string, string> param = new Dictionary<string, string>();
            param.Add("market_id", marketId);
            param.Add("side", side);
            param.Add("price", price);
            param.Add("amount", amount);
            string result = httpUtil.RequestHttpPost(url, param);
            return result;
        }
        
        public string CancelOrder(string orderId)
        {
            HttpUtilManagerBigOne httpUtil = HttpUtilManagerBigOne.GetInstance();
            string url = ApiEntryPoint + "viewer/orders/" + orderId + "/cancel";
            Dictionary<string, string> param = new Dictionary<string, string>();
            param.Add("order_id", orderId);
            string result = httpUtil.RequestHttpPost(url, param);
            return result;
        }
        
        public string CancelAllOrders(string marketId)
        {
            HttpUtilManagerBigOne httpUtil = HttpUtilManagerBigOne.GetInstance();
            string url = ApiEntryPoint + "viewer/orders/cancel_all";
            Dictionary<string, string> param = new Dictionary<string, string>();
            param.Add("market_id", marketId);
            string result = httpUtil.RequestHttpPost(url, param);
            return result;
        }
        
        public string GetWithdrawal()
        {
            HttpUtilManagerBigOne httpUtil = HttpUtilManagerBigOne.GetInstance();
            string url = ApiEntryPoint + "viewer/withdrawals";
            string param = "";
            string result = httpUtil.RequestHttpGet(url, param, true);
            return result;
        }
        
        public string GetDeposit()
        {
            HttpUtilManagerBigOne httpUtil = HttpUtilManagerBigOne.GetInstance();
            string url = ApiEntryPoint + "viewer/deposits";
            string param = "";
            string result = httpUtil.RequestHttpGet(url, param, true);
            return result;
        }
    }
    

    Step3 封装应用层

    以获取某交易对 marketId 市场挂单 买单Bid,卖单Ask 为例进行说明。

    public class BigOneUtility
    {
        private static readonly RestApiBigOne BigOneApi = new RestApiBigOne();
        
        public static void GetOrderBook(string marketId, out List<Ask> ask, out List<Bid> bid)
        {
            ask = default(List<Ask>);
            bid = default(List<Bid>);
    
            string json = BigOneApi.GetOrderBook(marketId);
            if (string.IsNullOrEmpty(json) == false)
            {
                StringReader sr = new StringReader(json);
                JsonTextReader jsonReader = new JsonTextReader(sr);
                JsonSerializer serializer = new JsonSerializer();
                GetOrderBookJson orderBook = serializer.Deserialize<GetOrderBookJson>(jsonReader);
    
                if (orderBook.data.asks != null)
                {
                    ask = orderBook.data.asks.OrderBy(a => a.price).ToList();
                }
                if (orderBook.data.bids != null)
                {
                    bid = orderBook.data.bids.OrderByDescending(a => a.price).ToList();
                }
            }
        }
    }
    

    <u>买单结构</u>

    public class Bid
    {
        /// <summary>
        /// bid price
        /// </summary>
        public string price { get; set; }
        /// <summary>
        /// bid amount
        /// </summary>
        public string amount { get; set; }
    }
    

    <u>卖单结构</u>

    public class Ask
    {
        /// <summary>
        /// ask price
        /// </summary>
        public string price { get; set; }
        /// <summary>
        /// ask amount
        /// </summary>
        public string amount { get; set; }
    }
    

    总结

    到此为止,向大家介绍了自己重构 BigOne API 的整体思路,即利用分层的思想把不同功能的对象放在不同的层中,来为上一层提供服务,减少对象之间的耦合。

    这是自动运行四个交易对 PRS-USDTBTM-USDTEOS-USDTONE-USDT 的截图,在这个市场里存在很多的套利机会,只要我们肯学习,再懂一些编程的技术,就会拥有大量的机会。

    自动化交易

    好的,今天就到这里吧! See You!

    对了,到目前为止已经有9名同学 通过解码 Huffman Code 得到团队的报名方式。

    团队的招新仍在进行中,对我们感兴趣的同学欢迎与我联系,我会亲自带着大家学习,一起成长!


    相关图文

    相关文章

      网友评论

          本文标题:技术图文:如何进行代码的重构?以封装 BigOne API 为例

          本文链接:https://www.haomeiwen.com/subject/eubjcctx.html