根据今年Crypto的表现,其实从CoinGlass的持仓&多空比&资金费率、聚合现货订单薄或者是全网期货持仓、链上数据、稳定币发行量、市占率等数据,这些东西对于行情来说并没有指示,只能起事后分析的作用,看多了反而使信息接受冗余。


虽然很多策略从三四年前甚至更远开始算回测,可以有N倍收益,但如果只是测试从3月14日到现在这半年内的数据,你很难看到有表现好的。


是的,意思就是如果在2015年投入$1,今天将收获$10,116USD。

但实际上从2024年初至今总收益仅为140%,更不用提利润在2021~2022横盘了一整年。

根据我看过一些策略的实际情况,经常发现利润回撤和修复的时间相当久。

于是窝老另写Strategy做BTC回测,结果超乎预料:

历史回测开始时间结束时间
回测&交易范围2024-01-01 08:002024-09-18 00:00

可以看到,这个策略不管遇上暴涨暴跌的单边行情,还是来回洗的横盘震荡,都可以稳定盈利(也就是低点不断抬高)。

而最大回撤是9.45%,这个利润/亏损比相当优秀,因为策略的数据表现优异(特别是最大回撤控制)意味着可以加大风险来换取超额复利利润。

DetailsVer. IVer. IIVer. IIIVer. IVVer. Ⅴ
净利润211.79%183.62%218.15%260.64%279.78%
最大亏损9.45%7.95%6.63%6.63%6.63%
胜率66.67%57.34%53.52%55.48%56.08%
夏普比率1.881.4831.8431.7911.678
索提诺比率N/AN/AN/AN/AN/A
盈利因子2.262.2192.3212.5392.652

设定两倍杠杆:

当杠杆x2,虽然此时的理论最高回撤相比原来提升了一倍,但因为杠杆*胜率*多次交易积累所带来的复利效应的原因,收益由211.79%超额提升到了779.78%。

由于极高的净利润/最大亏损比,所以数据层面的损失很小:

DetailsVer. IVer. IIVer. IIIVer. IVVer. Ⅴ
净利润779.78%643.80%827.34%1,089.60%1,216.98%
最大亏损18.56%15.50%12.93%12.93%12.93%
胜率66.67%57.34%53.52%55.48%56.08%
夏普比率1.7661.4331.7651.6841.583
索提诺比率N/AN/AN/AN/AN/A
盈利因子2.2262.0862.252.4592.63

Post Script:收益曲线顺滑版(单利)↓

DetailsVer. IVer. IIVer. IIIVer. IVVer. Ⅴ
净利润500.41%704.50%722.08%797.83%820.88%
最大亏损18.00%19.64%18.13%18.13%18.13%
胜率66.67%57.34%53.52%55.48%56.08%
夏普比率1.3930.780.8830.9230.861
索提诺比率N/AN/AN/AN/AN/A
盈利因子2.2952.4022.4272.6422.682

平衡最大可接受回撤的杠杆:

可以看到,继续放大回撤风险的情况下,收益直接来到了13,547.06%,只算纯利润是1倍杠杆情况下的63.96倍(不考虑投资基数),而最大回撤仅仅为原来(9.45%)的5.27倍。

这就是高胜率,优秀数据和风险控制好的情况下,放大杠杆带来的复利效应;坏处是随时有可能一笔交易让你亏损超过20%。

不过就超过2/3的胜率,以及各项优秀数据来看,你确实可以说,这种杠杆其实是可以上实盘的。

因为量化策略要解决的问题就是:

如何在控制亏损的情况下,最多的保留策略运行过程中获得的利润。

如果你的胜率足够高,能够有效限制最大回撤,开高杠杆(其实图中杠杆也不算高)是完全可行的。

DetailsVer. IVer. IIVer. IIIVer. IVVer. Ⅴ
净利润13,547.06%44,077.06%173,836.83%423,370.89%625,045. 00%
最大亏损49.86%49.86%49.85%49.85%49.85%
胜率66.67%57.34%53.52%55.48%56.08%
夏普比率1.4191.1921.3991.0971.048
索提诺比率N/A36.147N/AN/AN/A
盈利因子2.0511.7312.0272.1752.734

DetailsVer. IVer. IIVer. IIIVer. IVVer. Ⅴ
净利润26,290.76%123,504.04%485,789.49%1,497,634.32%2,393,310. 15%
最大亏损59.87%59.84%59.86%59.86%59.86%
胜率66.67%57.34%53.52%55.48%56.08%
夏普比率1.3151.1281.3320.9510.912
索提诺比率N/A27.655N/AN/AN/A
盈利因子1.9821.6531.9672.1162.783

数据堆彻

前面可以看到,在1倍杠杆的时候,夏普比率还能接近1.9,随着杠杆拉升,当回撤放大至60%,数据同时也降低到了1.3。

那么,有没有夏普比率奇高的策略呢?

窝老刚好也写出了一种可能:

1倍杠杆的情况下,和前面的对比其实没有区别,但是:

不夸张的说,就我有限的认知来看,这的确是我见过数据最好的Pine Script策略。

并且,这个版本的策略无限拉高杠杆时数据损失也很小:

直接回撤80%,看一眼数据:

这个夏普比率是怎么能维持在1.8的?

毕竟,前面的版本,回撤放大到60%,Sharpe就已经降到1.3了…


隐性未来预见:

量化策略回测中有很多陷阱,其中「未来函数」就是最容易导致误解的。

未来函数会引发重绘(Repainting),指的是图表数据后指针或是策略进出场点不一致,如果代码里有参数引发了重绘,策略的收益就会相差几倍。

也许不是刻舟求剑式指定历史高低点的过拟合来优化策略,但是不经意间会导致疏忽。

举例:参数「calc_on_order_fills」如果你设置为勾选,那么每一次的进场点会从OPEN而不是CLOSE计算,结果就是每一次交易至少有0.*%甚至*%的领先优势,以此为计算的亏损也会更少。

运行时如果你的代码有重绘的可能就会引发提示,同一个策略上引用未来函数会让收益率大幅增长。

比如:

如图所示,引用未来函数之后,净利润从1,100.09%直飙到1,930.45%,不仅改变了部分曲线,也放大了收益,显然脱离了真实情况,实盘运行起来的收益更接近图1。


量化策略的好处也许就在于:

你不用特别去关注Fed的动向、加息降息周期、扩表缩表进程、TGA抽取ON RRP的流动性给市场放水、耶伦偏短期的调整债券发行方式、美元指数、国债收益率、宏观经济以及美国通胀、失业率、泡沫化水平和市场流动性等数据。

也不会被4月13日凌晨的以色列导弹袭击以及7月31日BOJ加息导致的日元Carry Trade套利交易平仓带来的暴跌影响。

所有这些,5月1日洗盘,和5月20日放出的ETH ETF炒作消息的拉盘,都不会在情绪上另外作用到策略。

所谓量化策略,就是「计划你的交易,交易你的计划」。

忽略情绪,所有逻辑都基于冰冷的代码来执行。

完美符合经济学意义上的纯粹理性人。

31 responses to “Pine Script 策略”

  1. wp178245861 Avatar
    wp178245861

    量化和AI的普及终将改变整个金融市场的交易方式,遗憾的是大部分普通参与者都会成为时代的垫脚石。最近本人在大A为国化债,被庄家和量化反复收割,属实让人泪目。本着打不过就加入的心态开始学习Pine Script,但仓促间写成的代码回测总是惨不忍睹,又不死心,于是斗胆向博主请教Strategy,能否分享源码,以资共勉。
    感激不敬。

    此致 !

    敬礼!

    PowerArmor
    [email protected]

    1. Keihou Shuu Avatar

      我觉得你可以在Pine Script策略最下方加入如下代码来显示阶段收益,这样能更好优化:
      // 新增:每月收益率显示
      showMonthlyReturn = input.bool(true, “Show monthly earnings”, group = groupParameterSettings)
      fontSizeOptions = input.string(“large”, title = “Font Size”, options = [“tiny”, “small”, “normal”, “large”, “huge”], group = groupParameterSettings)

      var month_return = map.new<int, float>()
      var month_strat_money = map.new<int, float>()
      var years = array.new()

      int now = year * 10000 + month
      flag = true
      for mon in map.keys(month_strat_money)
      if now == mon
      flag := false
      if flag == true
      map.put(month_strat_money, now, strategy.equity)
      start_money = map.get(month_strat_money, now)
      map.put(month_return, now, (strategy.equity / start_money – 1.0) * 100)
      if years.includes(year) == false
      years.push(year)
      labels = array.from(“”, “Jan”, “Feb”, “Mar”, “Apr”, “May”, “Jun”, “Jul”, “Aug”, “Sep”, “Oct”, “Nov”, “Dec”, “Q1”, “Q2”, “Q3”, “Q4”, “Annual Return”)
      make_table(years, labels) =>
      rows = array.size(years) + 1
      columns = array.size(labels)
      backgroundColor = color.new(color.rgb(212, 212, 212), 0)
      winColor = color.rgb(81, 127, 70)
      lossColor = color.rgb(167, 76, 70)
      e = table.new(position.bottom_right, rows = rows, columns = columns, bgcolor = backgroundColor, border_width = 2, border_color = color.black, frame_width = 2, frame_color = color.black)
      for i = 0 to rows – 1
      for j = 0 to columns – 1
      table.cell_set_text_color(e, j, i, color.black)
      table.cell_set_text_size(e, j, i, fontSizeOptions)
      if i > 0 and j > 0
      table.cell_set_bgcolor(e, j, i, color.rgb(161, 162, 165))
      table.cell_set_text_color(e, j, i, color.white)
      for j = 0 to columns – 1
      table.cell_set_text(e, j, 0, array.get(labels, j))
      for i = 1 to rows – 1
      table.cell_set_text(e, 0, i, str.tostring(array.get(years, i – 1)))
      start_year = array.get(years, 0)
      sum_year = array.new(array.size(years), 1.0)
      quarterly_sum = array.new
      (4 * array.size(years), 1.0)
      for before in map.keys(month_return)
      nowyear = before / 10000
      nowmonth = before % 10000
      beforevalue = array.get(sum_year, nowyear – start_year)
      beforevalue *= (1.0 + map.get(month_return, before) / 100)
      array.set(sum_year, nowyear – start_year, beforevalue)
      return_value = map.get(month_return, before)
      return_value_str = return_value >= 0 ? “+” + str.tostring(return_value, “#.##”) : str.tostring(return_value, “#.##”)
      table.cell_set_bgcolor(e, nowmonth, (nowyear – start_year + 1), return_value >= 0 ? winColor : lossColor)
      table.cell_set_text(e, nowmonth, (nowyear – start_year + 1), return_value_str + “%”) // 添加百分比符号
      // 计算季度收益
      quarter = (nowmonth – 1) / 3
      quarterly_index = (nowyear – start_year) * 4 + quarter
      quarterly_value = array.get(quarterly_sum, quarterly_index)
      quarterly_value *= (1.0 + map.get(month_return, before) / 100)
      array.set(quarterly_sum, quarterly_index, quarterly_value)
      for i = 1 to rows – 1
      return_value = (array.get(sum_year, i – 1) – 1.0) * 100
      return_value_str = return_value >= 0 ? “+” + str.tostring(return_value, “#.##”) : str.tostring(return_value, “#.##”)
      table.cell_set_bgcolor(e, columns – 1, i, return_value >= 0 ? winColor : lossColor)
      table.cell_set_text(e, columns – 1, i, return_value_str + “%”) // 添加百分比符号
      // 显示季度收益
      for q = 0 to 3
      quarterly_return = (array.get(quarterly_sum, (i – 1) * 4 + q) – 1.0) * 100
      quarterly_return_str = quarterly_return >= 0 ? “+” + str.tostring(quarterly_return, “#.##”) : str.tostring(quarterly_return, “#.##”)
      table.cell_set_bgcolor(e, 12 + q + 1, i, quarterly_return >= 0 ? winColor : lossColor)
      table.cell_set_text(e, 12 + q + 1, i, quarterly_return_str + “%”) // 添加百分比符号
      if showMonthlyReturn
      make_table(years, labels)

    2. Keihou Shuu Avatar

      对了,如果是单利情况下,也就是除了百分比以外还显示详细P&L金额的,可以用这个版本:
      // 新增:每月收益金额显示
      showMonthlyReturn = input.bool(true, “Show monthly earnings”, group = groupParameterSettings)
      fontSizeOptions = input.string(“normal”, title = “Font Size”, options = [“tiny”, “small”, “normal”, “large”, “huge”], group = groupParameterSettings)

      // 初始化变量
      var month_return = map.new<int, float>()
      var month_strat_money = map.new<int, float>()
      var month_return_percent = map.new<int, float>()
      var years = array.new()

      // 计算当前月份的收益
      int now = year * 10000 + month
      if not map.contains(month_strat_money, now)
      map.put(month_strat_money, now, strategy.equity)
      start_money = map.get(month_strat_money, now)
      month_change = strategy.equity – start_money
      month_return_percent_value = (strategy.equity / start_money – 1.0) * 100
      map.put(month_return, now, month_change)
      map.put(month_return_percent, now, month_return_percent_value)
      if not years.includes(year)
      years.push(year)

      // 标签数组
      labels = array.from(“”, “Jan”, “Feb”, “Mar”, “Apr”, “May”, “Jun”, “Jul”, “Aug”, “Sep”, “Oct”, “Nov”, “Dec”, “Q1”, “Q2”, “Q3”, “Q4”, “Annual Return”)

      // 创建表格函数
      make_table(years, labels) =>
      rows = array.size(years) + 1
      columns = array.size(labels)
      backgroundColor = color.new(color.rgb(212, 212, 212), 0)
      winColor = color.rgb(81, 127, 70)
      lossColor = color.rgb(167, 76, 70)
      e = table.new(position.bottom_right, rows = rows, columns = columns, bgcolor = backgroundColor, border_width = 2, border_color = color.black, frame_width = 2, frame_color = color.black)

      // 设置表格样式
      for i = 0 to rows - 1
      for j = 0 to columns - 1
      table.cell_set_text_color(e, j, i, color.black)
      table.cell_set_text_size(e, j, i, fontSizeOptions)
      if i > 0 and j > 0
      table.cell_set_bgcolor(e, j, i, color.rgb(161, 162, 165))
      table.cell_set_text_color(e, j, i, color.white)
      for j = 0 to columns - 1
      table.cell_set_text(e, j, 0, array.get(labels, j))
      for i = 1 to rows - 1
      table.cell_set_text(e, 0, i, str.tostring(array.get(years, i - 1)))

      // 初始化年度和季度收益数组
      start_year = array.get(years, 0)
      sum_year = array.new<float>(array.size(years), 0.0)
      quarterly_sum = array.new<float>(4 * array.size(years), 0.0)
      quarterly_sum_percent = array.new<float>(4 * array.size(years), 1.0)
      annual_sum_percent = array.new<float>(array.size(years), 1.0)

      // 计算收益
      for before in map.keys(month_return)
      nowyear = before / 10000
      nowmonth = before % 10000
      year_index = nowyear - start_year
      quarter = (nowmonth - 1) / 3
      quarterly_index = year_index * 4 + quarter

      // 更新年度收益
      array.set(sum_year, year_index, array.get(sum_year, year_index) + map.get(month_return, before))
      array.set(annual_sum_percent, year_index, array.get(annual_sum_percent, year_index) * (1.0 + map.get(month_return_percent, before) / 100))

      // 更新季度收益
      array.set(quarterly_sum, quarterly_index, array.get(quarterly_sum, quarterly_index) + map.get(month_return, before))
      array.set(quarterly_sum_percent, quarterly_index, array.get(quarterly_sum_percent, quarterly_index) * (1.0 + map.get(month_return_percent, before) / 100))

      // 设置单元格内容
      return_value = map.get(month_return, before)
      return_percent = map.get(month_return_percent, before)
      return_value_str = return_value >= 0 ? "+" + str.tostring(return_value, "#.##") : str.tostring(return_value, "#.##")
      return_percent_str = return_percent >= 0 ? "+" + str.tostring(return_percent, "#.##") : str.tostring(return_percent, "#.##")
      table.cell_set_bgcolor(e, nowmonth, year_index + 1, return_value >= 0 ? winColor : lossColor)
      table.cell_set_text(e, nowmonth, year_index + 1, return_value_str + "\n(" + return_percent_str + "%)")

      // 显示季度和年度收益
      for i = 1 to rows - 1
      year_index = i - 1
      return_value = array.get(sum_year, year_index)
      return_percent = (array.get(annual_sum_percent, year_index) - 1.0) * 100
      return_value_str = return_value >= 0 ? "+" + str.tostring(return_value, "#.##") : str.tostring(return_value, "#.##")
      return_percent_str = return_percent >= 0 ? "+" + str.tostring(return_percent, "#.##") : str.tostring(return_percent, "#.##")
      table.cell_set_bgcolor(e, columns - 1, i, return_value >= 0 ? winColor : lossColor)
      table.cell_set_text(e, columns - 1, i, return_value_str + "\n(" + return_percent_str + "%)")
      for q = 0 to 3
      quarterly_return = array.get(quarterly_sum, year_index * 4 + q)
      quarterly_return_percent = (array.get(quarterly_sum_percent, year_index * 4 + q) - 1.0) * 100
      quarterly_return_str = quarterly_return >= 0 ? "+" + str.tostring(quarterly_return, "#.##") : str.tostring(quarterly_return, "#.##")
      quarterly_return_percent_str = quarterly_return_percent >= 0 ? "+" + str.tostring(quarterly_return_percent, "#.##") : str.tostring(quarterly_return_percent, "#.##")
      table.cell_set_bgcolor(e, 12 + q + 1, i, quarterly_return >= 0 ? winColor : lossColor)
      table.cell_set_text(e, 12 + q + 1, i, quarterly_return_str + "\n(" + quarterly_return_percent_str + "%)")

      if showMonthlyReturn
      make_table(years, labels)

  2. Lora354 Avatar
    Lora354
  3. Reed4111 Avatar
    Reed4111
  4. solitaire games Avatar

    Roulette’s randomness is fascinating, mirroring the unpredictable nature of shuffling cards! Speaking of accessible games, I recently discovered solitaire games – instant play, no downloads needed, a nice mental break! It’s cool how tech keeps classic games alive.

  5. t128128 Avatar

    Yo, t128128, let me tell ya! They’ve got a respectable platform. Easy to navigate, and the betting odds seem alright. Plus points for the mobile site. Def worth a looksee: t128128

  6. jili7 Avatar

    Trying out casual gambling platforms is always fun, and Jili brings a fresh vibe with its AI-boosted gameplay. The Jili777 app feels smooth, with a great selection of slots and live games that keep things exciting without the stress. Definitely worth a spin!

  7. 939gamelogin Avatar

    Been messing around with 939gamelogin, and the login was super easy. No problems logging in, smooth sailing all the way! Give it a shot: 939gamelogin

  8. nilfortuneonline Avatar

    Alright folks, heard about nilfortuneonline and thought I’d see what the buzz is about. Seems promising, gonna give it a go. Find out more at nilfortuneonline.

  9. fun222 Avatar

    Having a blast over at fun222! Lots of fast games, the slots are fun, and the bonuses keep things interesting. I’ve recommended it to me friends as well, try it out for yourself!: fun222

  10. one888vn.com Avatar

    Alright gamers, one888vn.com caught my eye. The vibes are good and the games seem legit. Give it a whirl and see if it’s your jam! You can find it at one888vn.com.

  11. tai99win Avatar

    Heard some buzz about Tai99win and had to see for myself. Site looks fresh and the games are decent. If you are feeling lucky, see if this is for you:tai99win.

  12. 1plusph Avatar

    1plusph? Seems like another one of these online gaming sites. I haven’t spent a ton of time on the site, but the games seem standard. Nothing too crazy. You can find it at 1plusph if you want to judge for yourself.

  13. 777pubapp Avatar

    The 777pubapp is alright. I loaded it up on my phone and it’s reasonably smooth. The graphics don’t amaze me, but it works as expected. Log in and play over at 777pubapp!

  14. 777pubclub Avatar

    Ah, 777pubclub. Feels a bit old-school, but sometimes that’s what you’re looking for, right? A classic feel with familiar games. Check it out at 777pubclub.

  15. 1777sxgame Avatar

    1777sxgame, is legit, yo. Found exactly what I was searching for. Highly suggest 1777sxgame

  16. 668jilicom Avatar

    Digging the games on 668jilicom! Wide selection and the site is pretty responsive. Definitely worth a try if you’re looking for something new. Check em out: 668jilicom

  17. 80jilinet Avatar

    80jilinet is where I go to chill and play. The site’s cool, and I’ve been lucky a couple of times. Giving it a thumbs up! Go check: 80jilinet

  18. hytalejuego Avatar

    The games here are pretty crazy you will lose track of time. Check out the link below hytalejuego

  19. c999 Avatar

    C999 is a interesting site. I was drawn to the interface, which is pretty unique. You will love it! Try c999.

  20. cm88com Avatar

    CM88com? If you are finding a place to relax, you can try the CM88com! Click here: cm88com.

  21. ph77 Avatar

    PH77 Online Casino Philippines: Fast PH77 Login, Register & App Download for Premium PH77 Slot Games. Join PH77 Online Casino Philippines for the ultimate gaming experience! Enjoy fast PH77 login, easy PH77 register, and secure PH77 app download for premium PH77 slot games. Start winning today at the most trusted PH77 online casino. visit: ph77

  22. esporte bet nacional Avatar

    Curto demais apostar em Esporte Bet Nacional! Sempre tem promoções boas e os pagamentos são super rápidos. Top demais! esporte bet nacional

  23. bet69 keo nha cai Avatar

    For those keo nha cai plays, Bet69 is worth a look. I’ve seen some solid odds there. Could be your lucky spot! bet69 keo nha cai

  24. bet69 kèo nhà cái hôm nay Avatar

    Need the latest kèo nhà cái for today’s games? Bet69 and Keonhacaibet69.org are worth checking. Could be some good bets in there. Take a peek: bet69 kèo nhà cái hôm nay

  25. kkkkjl Avatar

    Your systematic approach to strategy backtesting is impressive – particularly the 6.63% max drawdown control across different versions. This risk management focus mirrors what we prioritize in gaming platform development. When optimizing algorithmic trading systems or user experiences like kkkkjl slot download features, sustainable performance beats theoretical maximums every time.

  26. 1gocasinobr Avatar

    Just had a go on 1gocasinobr. Really easy to navigate and get playing. Selection of games is alright! See what you think: 1gocasinobr

  27. djbaji Avatar

    Just started using djbaji and I’m kinda hooked. It’s easy to use and seems pretty fair. If you are wanting to try it check out djbaji. Could be worth checking out.

  28. y8888game Avatar

    If you just wanna chill and play a simple game, then y8888game is the site for you! Check it out and see if it’s your jam: y8888game. You might just like it.

Leave a Reply to nilfortuneonlineCancel reply


Producer's Personal Blog

Copyright © 2024 Keihou Shuu