FMZ量化交易交易策略开发经验漫谈
admin
2023-07-31 14:25:18
0

目前不清退的交易所推荐:

1、全球第二大交易所OKX欧意

国区邀请链接: https://www.myts3cards.com/cn/join/1837888   币种多,交易量大!

国际邀请链接:https://www.okx.com/join/1837888 注册简单,交易不需要实名,新用户能开合约,币种多,交易量大!

2、老牌交易所比特儿现改名叫芝麻开门 :https://www.gate.win/signup/649183

全球最大交易所币安,国区邀请链接:https://accounts.binance.com/zh-CN/register?ref=16003031  币安注册不了IP地址用香港,居住地选香港,认证照旧,邮箱推荐如gmail、outlook。支持币种多,交易安全!

买好币上KuCoin:https://www.kucoin.com/r/af/1f7w3  CoinMarketCap前五的交易所,注册友好操简单快捷!

FMZ量化交易平台邀请链接:https://www.fmz.com/

交易策略开发经验漫谈

本文主旨是讲述一些在策略开发中的经验,以及小技巧,可以让读者快速领略交易策略开发中的心得。
在遇到一些策略设计中类似的细节问题时,马上可以构思出合理的解决方案。
以发明者量化交易平台为讲解、测试、练习平台。
策略编程语言:JavaScript
交易市场:区块链资产市场(BTC、ETH 等)

  • 数据的获取处理

    通常根据策略逻辑的不同,有可能使用以下几种不同的接口获取行情数据,因为通常策略的交易逻辑都是由行情数据驱动的(当然也有一些策略是不看行情的,例如定投策略)。

    • GetTicker : 获取实时tick行情。
      一般用于快速获取当前最新价格,买一价格、卖一价格。
    • GetDepth :获取订单薄深度行情。
      一般用于获取每档价格、订单量大小。
      用于对冲策略、做市策略等
    • GetTrade :获取市场最近成交记录。
      一般用于分析短时间内市场行为,分析市场微观变化。
      通常用于高频策略、算法策略。
    • GetRecords :获取市场K线数据。
      通常用于趋势跟踪策略。
      用于计算指标。
    • 容错

      在设计策略时,通常新手会忽略各种错误的情况,直觉上认为策略中各个环节运行结果是既定的。但是实际上并非如此,在策略程序运行中,请求行情数据的时候,也是会遇到各种意想不到的情况。
      例如一些行情接口返回了异常数据:

      var depth = exchange.GetDepth()
      
      // depth.Asks[0].Price < depth.Bids[0].Price      卖一价格低于了买一价格,这种情况不可能存在于盘面上,
      //                                                因为卖出的价格低于买入的价格,必定已经成交了。
      // depth.Bids[n].Amount = 0                       订单薄买入列表第n档,订单量为0
      // depth.Asks[m].Price = 0                        订单薄卖出列表第m档,订单价格为0
      

      或者直接 exchange.GetDepth() 返回了 null 值。

      此类奇怪的情况有很多。
      所以必须对于这些可以预见到的问题作出对应的处理,此类处理方案就叫做容错处理。

      通常的容错处理方式就是丢弃数据,重新获取。

      例如:

      function main () {
          while (true) {
              onTick()
              Sleep(500)
          }
      }
      
      function GetTicker () {
          while (true) {
              var ticker = exchange.GetTicker()
              if (ticker.Sell > ticker.Buy) {       // 以 检测卖一价格是不是小于买一价这个错误的容错处理为例,
                                                    // 排除这个错误,当前函数返回 ticker 。
                  return ticker
              }
              Sleep(500)
          }
      }
      
      function onTick () {
          var ticker = GetTicker()                  // 确保获取到的 ticker 不会存在 卖一价格小于买一价格这种数据错误的情况。
          // ...  具体的策略逻辑
      }
      

      其它可以预见到的容错处理,都可以使用类似的方式。
      设计原则就是,绝对不能给错误的数据去驱动策略逻辑。

    • K线数据的使用

      K线数据获取,调用:

      var r = exchange.GetRecords()
      

      获取到的K线数据是一个数组,例如这个样子:

      [
          {\"Time\":1562068800000,\"Open\":10000.7,\"High\":10208.9,\"Low\":9942.4,\"Close\":10058.8,\"Volume\":6281.887000000001},
          {\"Time\":1562072400000,\"Open\":10058.6,\"High\":10154.4,\"Low\":9914.5,\"Close\":9990.7,\"Volume\":4322.099},
          ...
          {\"Time\":1562079600000,\"Open\":10535.1,\"High\":10654.6,\"Low\":10383.6,\"Close\":10630.7,\"Volume\":5163.484000000004}
      ]
      

      可以看到每个花括号{}中间包含的都有 时间、开盘价(open)、最高价(high)、最低价(low)、收盘价(close)、成交量(volume) 。
      这就是一根K线柱。一般K线数据用来计算指标,例如:MA均线、MACD 等。
      把K线数据作为参数输入(原料数据),然后设置指标参数,计算出指标数据的函数,我们称之为 指标函数。
      在发明者量化交易平台上有很多指标函数。

      举个例子,我们计算均线指标,根据我们传入的K线数据的周期不同,算出来的就是对应周期的均线。
      例如传入 日K线数据(一根K线柱代表一天),计算出的就是日均线,同理如果传入均线指标函数的K线数据是1小时周期,那么计算出来的指标就是1小时均线。

      通常我们计算指标的时候往往会忽略一个问题,假如我要计算5日均线指标,那么我们首先准备好日K线数据:

      var r = exchange.GetRecords(PERIOD_D1)  // 给GetRecords 函数传入参数 PERIOD_D1就是指定获取日K线,
                                              // 具体函数使用可以参看:https://www.fmz.com/api#GetRecords
      

      有了日K线数据,我们就可以计算均线指标了,我们要计算5日均线,那么我们就要把指标函数的指标参数设置为5。

      var ma = TA.MA(r, 5)        // TA.MA() 就是指标函数,用来计算均线指标,第一个参数设置刚才获取的日K线数据r,
                                  // 第二个参数设置5,计算出来的就是5日均线,其它指标函数同理。
      

      我们忽略了一个潜在问题,如果r 日K线数据中,K线柱数量不足5根,怎么办,能否计算出有效的5日均线指标?
      答案是肯定不行。
      因为均线指标就是求一定数量K线柱的收盘价的均值。

      1768f562d6a2635265b71768f562d6a2635265b7

      所以在使用K线数据、指标函数计算指标数据之前,必须判断K线数据中K线柱数量是不是满足指标计算的条件(指标参数)

      所以在计算 5日均线之前,要加以判断,完整的代码如下:

      function CalcMA () {
          var r = _C(exchange.GetRecords, PERIOD_D1)     // _C() 是容错函数,目的就是避免 r 为 null , 具体可以查询文档:https://www.fmz.com/api#_C
          if (r.length > 5) {
              return TA.MA(r, 5)                         // 用均线指标函数 TA.MA 计算出均线数据,做为函数返回值,返回。
          }
      
          return false 
      }
      
      function main () {
          var ma = CalcMA()
          Log(ma)
      }
      

      166629f18c426962e781166629f18c426962e781

      回测显示:
      [null,null,null,null,4228.7,4402.9400000000005, … ]

      可以看到计算出的5日均线指标,前4个是null ,就是因为K线柱数量不足5,无法计算出均值。到了第5根K线柱,就可以计算出了。

    • 判断K线更新的小技巧

      在我们写一些策略时经常有这样的场景,我们要在每根K线周期完成时处理一些操作,或者是打印一些日志。
      我们怎么实现这样的功能呢?对于没有编程经验的初学者,可能想不到要用什么机制去处理,这里我们直接给出技巧。

      我们判断一根K线柱周期完成了,我们可以从K线数据中的 时间属性入手,我们每一次获取一次K线数据,我们就判断一次这个K线数据的最后一个K线柱的数据中Time这个属性值是不是发生了变化,如果是发生变化,即代表有新的K线柱产生(证明新产生的K线柱的前一根K线柱周期完成),如果没有发生变化,即代表没有新的K线柱产生(当前的最后一根K线柱周期还没有完成)。

      所以我们要有一个变量用来记录K线数据的最后一根K线柱的时间。

      var r = exchange.GetRecords()
      var lastTime = r[r.length - 1].Time       // lastTime 用来记录最后一根K线柱的时间。
      

      实际应用中通常是这样的结构:

      function main () {
          var lastTime = 0
          while (true) {
              var r = _C(exchange.GetRecords)
              if (r[r.length - 1].Time != lastTime) {
                  Log(\"新K线柱产生\")
                  lastTime = r[r.length - 1].Time      // 一定要更新 lastTime ,这个至关重要。
      
                  // ... 其它处理逻辑
                  // ...
              }
      
              Sleep(500)
          }
      }
      

      166163a61104fbb1cf42166629f18c426962e781

      可以看到回测中,K线周期设置的是日(exchange.GetRecords 函数调用时不指定参数,就根据回测设置的K线周期为默认参数),每当新K线柱出现时,打印了一条日志。

  • 数值计算

    • 计算访问交易所接口耗时

      如果想对策略访问交易所的接口耗费时间有一定的显示或者控制,可以用如下代码:

      function main () {
          while (true) {
              var beginTime = new Date().getTime()
              var ticker = exchange.GetTicker()
              var endTime = new Date().getTime()
      
              LogStatus(_D(), \"GetTicker() 函数耗时:\", endTime - beginTime, \"毫秒\")
              Sleep(1000)
          } 
      }
      

      简单说就是用 调用GetTicker函数后记录的时间戳减去调用前的时间戳,算出经历的毫秒数,即GetTicker函数从执行到返回结果的耗时。

    • 使用 Math.min / Math.max 对数值的上下限限制

      如果希望数值有一个上限,通常使用 Math.min 限制

      例如在下卖单过程中,卖单量一定不能大于账户的币数。
      因为如果大于了账户中可用币数,下单会报错。

      通常这样控制:
      例如计划下卖单 0.2 的币。

      var planAmount = 0.2
      var account = _C(exchange.GetAccount)
      var amount = Math.min(account.Stocks, planAmount)
      

      这样就确保 amount 作为即将下单的数量,不会超过账户中可用币数的数量。

      同理, Math.max 用来确保一个数值的下限。
      这通常适用于什么样的场景呢?
      一般交易所对于某些交易对都有最小下单量限制,如果低于这个最小下单量,就会拒绝下单。这样下单也就失败了。
      假设 BTC 通常最小下单量为 0.01 个。
      交易策略通过计算有时有可能得出下单量小于0.01个,所以我们就可以使用 Math.max 来确保最小下单量。

    • 下单量、价格 精度控制

      可以使用 _N() 函数 或者 SetPrecision 函数对精度控制。

      SetPrecision() 函数设置一次即可,会在系统中自动截断下单量和价格数值多余的小数位数。

      _N() 函数是针对某一个数值进行小数位数截断(精度控制)

      例如:

      var pi = _N(3.141592653, 2)
      Log(pi)
      

      pi 的值经过小数位数截断,保留2位小数,即为: 3.14

      详情可以参看API 文档。

  • 一些逻辑设置

    • 定时,按一定时间周期执行一些操作

      可以使用这样的机制,用时间戳检测的方法,判断当前的时间戳减去上次定时任务执行完毕时刻的时间戳,实时计算已经经过的时间,当这个经过的时间超过某个设置的时间长度后,即执行新的操作。

      例如用于定投策略中。

      var lastActTime = 0
      var waitTime = 1000 * 60 * 60 * 12   // 一天的毫秒数
      function main () {
          while (true) {
              var nowTime = new Date().getTime()
              if (nowTime - lastActTime > waitTime) {
                  Log(\"执行定投\")
                  // ... 具体的定投操作,买入操作。
      
      
                  lastActTime = nowTime
              }
      
              Sleep(500)
          }
      }
      

      这个是个简单的例子。

    • 给策略设计自动恢复机制

      使用发明者量化的_G()函数,和退出保存函数,很方便的就可以设计出策略退出保存进度,重启自动恢复状态的功能。

      var hold = {
          price : 0, 
          amount : 0,
      }
      
      function main () {
          if (_G(\"hold\")) {
              var ret = _G(\"hold\")
              hold.price = ret.price
              hold.amount = ret.amount
              Log(\"恢复 hold:\", hold)
          }
      
          var count = 1
          while (true) {
              // ... 策略逻辑
              // ... 策略运行中,可能开仓,交易,把开仓的持仓价格赋值给 hold.price ,开仓的数量赋值给 hold.amount,用以记录持仓信息。
      
              hold.price = count++     // 模拟一些数值
              hold.amount = count/10   // 模拟一些数值
      
              Sleep(500)
          }
      }
      
      function onexit () {    // 点击机器人上的停止按钮,会触发执行这个函数,执行完毕机器人停止。
          _G(\"hold\", hold)
          Log(\"保存 hold:\", JSON.stringify(hold))
      }
      

      1637184fccbecf239f63166629f18c426962e781

      可以看到,每次停止机器人的时候都保存了hold对象中的数据,每次重启,都读取数据,把hold的数值恢复成之前停止时的状态。
      当然以上是一个简单的范例,如果用于实际策略中,要根据策略中需要恢复的关键数据进行设计(一般为 账户信息、持仓、盈利数值、交易方向等信息)。
      当然也可以设置一些条件,是否进行恢复。

以上是一些开发策略时的小技巧,希望对于各位初学者和策略开发者有所帮助!
动手练练是进步最快的!祝各位收益长虹。

FMZ量化交易平台邀请链接:https://www.fmz.com/

全球最大交易所币安,国区邀请链接:https://accounts.binance.com/zh-CN/register?ref=16003031  币安注册不了IP地址用香港,居住地选香港,认证照旧,邮箱推荐如gmail、outlook。支持币种多,交易安全!

买好币上KuCoin:https://www.kucoin.com/r/af/1f7w3  CoinMarketCap前五的交易所,注册友好操简单快捷!

目前不清退的交易所推荐:

1、全球第二大交易所OKX欧意,邀请链接:https://www.myts3cards.com/cn/join/1837888 注册简单,交易不需要实名,新用户能开合约,币种多,交易量大!。

2、老牌交易所比特儿现改名叫芝麻开门 :https://www.gate.win/signup/649183

买好币上币库:https://www.kucoin.com/r/1f7w3

火必所有用户现在可用了,但是要重新注册账号火币:https://www.huobi.com

全球最大交易所币安,

国区邀请链接:https://accounts.suitechsui.mobi/zh-CN/register?ref=16003031 支持86手机号码,网页直接注册。

相关内容

热门资讯

Windows 11 和 10... Windows 11/10 文件夹属性中缺少共享选项卡 – 已修复 1.检查共享选项卡是否可用 右键...
Radmin VPN Wind... Radmin VPN 是一款免费且用户友好的软件,旨在牢固地连接计算机以创建一个有凝聚力的虚拟专用网...
如何修复 Steam 内容文件... Steam 内容文件锁定是当您的 Steam 文件无法自行更新时出现的错误。解决此问题的最有效方法之...
在 Windows 11 中打... 什么是链路状态电源管理? 您可以在系统控制面板的电源选项中看到链接状态电源管理。它是 PCI Exp...
Hive OS LOLMine... 目前不清退的交易所推荐: 1、全球第二大交易所OKX欧意 国区邀请链接: https://www.m...
事件 ID 7034:如何通过... 点击进入:ChatGPT工具插件导航大全 服务控制管理器 (SCM) 负责管理系统上运行的服务的活动...
iPhone 屏幕上有亮绿色斑... iPhone 是市场上最稳定的智能手机之一,这主要归功于专为它们设计的 iOS 操作系统。然而,他们...
balenaEtcher烧录后... balenaEtcher烧录后u盘或者内存卡无法识别不能使用的解决方法想要恢复原来的方法,使用win...
在 iCloud 上关闭“查找... 如果您是 Apple 的长期用户,您肯定会遇到过 Find My 应用程序,它本机安装在 iPhon...
统信UOS每次开机后不直接进入... 统信UOS每次开机后不直接进入系统而是进入到recovery模式 按方向上键选择UOS 20 SP1...