输入/搜索内容
欢迎使用发明者量化交易平台
编程语言
JavaScript
TypeScript
Python
C++
My语言(麦语言)
PINE语言
Blockly可视化
Workflow工作流
密钥安全性
实盘
策略库
托管者
部署托管者
一键租用托管者
手动部署托管者
托管者操作注意事项
全局指定IP地址
命令行版本托管者程序的参数
实盘数据迁移
托管者监控
交易所
策略编辑器
回测系统
策略入口函数
策略框架与API函数
模板类库
策略参数
交互控件
期权交易
C++策略编写说明
JavaScript策略编写说明
Web3
内置库
扩展API接口
MCP 服务
交易终端
数据探索
Alpha因子分析工具
通用协议
调试工具
远程编辑
完整策略的导入与导出
多语言支持
实盘、策略分组
实盘展示
策略分享与出租
实盘消息推送
实盘报错、异常退出的常见原因
交易所特殊说明

当您完成量化交易策略的设计后,如何验证策略的逻辑正确性、收益预期等关键指标?显然不能直接使用真实资金在市场中测试。正确的做法是使用历史数据对策略进行回测,通过分析策略在历史行情中的表现来评估其盈利能力和风险特征。

发明者量化交易平台将回测模式分为实盘级 Tick回测和模拟级 Tick回测。实盘级 Tick回测完全基于完整的历史数据进行回测;模拟级 Tick回测则根据真实K线数据生成tick数据来进行回测。两者都基于真实历史数据进行回测,但实盘级 Tick回测的数据更精准,结果更加可信。需要注意的是,回测仅反映策略在历史数据下的表现,历史数据并不能完全代表未来的行情,因此对待回测结果应保持理性、客观的态度。

模拟级 Tick回测根据底层K线周期生成模拟的tick数据,每个底层K线周期上最多生成12个回测时间点。而实盘级 Tick回测使用真实收集的逐秒tick数据,数据量大,回测速度较慢,因此不适合回测特别长的时间范围。FMZ量化的回测机制允许策略在一根K线上进行多次交易,避免了仅能在收盘价成交的局限性,在保证精准度的同时兼顾了回测速度。

回测系统机制说明

  • 模拟级 Tick
    模拟级 Tick回测根据回测系统的底层K线数据,按照特定算法在给定的底层K线Bar的最高价、最低价、开盘价、收盘价构成的价格框架内模拟生成tick数据进行回测,作为回测时间序列上的实时tick数据,在策略程序调用接口时返回。具体可参考:回测系统模拟级别机制说明

  • 实盘级 Tick
    实盘级别回测使用Bar时间序列中的真实tick级别数据。对于基于tick级别数据的策略,使用实盘级别回测更贴近实际情况。实盘级别回测的tick是真实记录的数据,并非模拟生成。支持深度数据、市场成交记录数据回放,支持自定义深度,支持分笔数据。实盘级别回测数据最大支持50MB,在数据上限内不限制回测时间范围。如需尽可能增大回测时间范围,可降低深度档位数值设置,不使用分笔数据以扩展回测时间范围。调用GetDepthGetTrades函数获取回放行情数据。在时间轴上某个行情数据时刻,调用GetTickerGetTradesGetDepthGetRecords,不会多次推动时间在回测时间轴上移动(不会触发跳转到下一个行情数据时刻)。对于以上某个函数的重复调用,将推动回测时间在回测时间轴上移动(跳转到下一个行情数据时刻)。回测时使用实盘级别回测不宜选择过早的时间,因为过早的时间段可能没有实盘级别数据。

实盘级Tick模拟级Tick模式的回测系统成交撮合机制:订单成交撮合按照见价成交、全量成交进行。因此回测系统中无法测试部分成交的场景。

以下测试代码针对不同的数据粒度(A. 实盘级别回测、B. 模拟级别回测(较小底层K线周期)、C. 模拟级别回测(较大底层K线周期)等)会呈现不同的表现。交易次数和盈亏结果均会有所差异。进行回测时应尽可能保持较小的数据粒度。虽然数据粒度较大时回测速度可能更快,但所得结果可能缺乏客观性。

javascript
/*backtest start: 2025-04-01 08:00:00 end: 2025-04-18 00:00:00 period: 1m exchanges: [{"eid":"Binance","currency":"BTC_USDT","balance":1000000}] mode: 1 */ var delta = 50 var lotSize = 0.001 var lastPrice = null var direction = null function main() { while (true) { var ticker = _C(exchange.GetTicker) if (!lastPrice) { lastPrice = ticker.Last } var diff = ticker.Last - lastPrice if ((!direction || direction == "long") && diff >= delta) { // 价格上涨超过阈值 -> 做空 exchange.Sell(ticker.Last, lotSize) Log("Short @", ticker.Last) direction = "short" } else if ((!direction || direction == "short") && diff <= -delta) { // 价格下跌超过阈值 -> 做多 exchange.Buy(ticker.Last, lotSize) Log("Long @", ticker.Last) direction = "long" } // Tick模式中应尽可能短,K线回测中无影响 Sleep(100) } }

回测系统支持:JavaScriptTypeScriptPythonC++PINEMy语言Blockly可视化、Workflow工作流编写设计的策略进行回测测试。

1、JavaScriptC++ 策略回测在浏览器端进行,JavaScriptC++ 语言的策略在实盘、回测运行时无需安装任何其他软件、库或模块。

2、Python 语言的策略回测在托管者上进行,可以在FMZ量化的公共服务器上回测,也可以在用户自己的托管者上回测。实盘和回测都依赖托管者所在系统上安装的Python环境,如需使用特定库,需自行安装。FMZ量化的公共服务器仅支持常用的 Python 库。

3、支持 JavaScript 语言的策略在回测时于Chrome浏览器DevTools中进行调试,参考说明

4、Workflow 工作流策略支持回测,可可视化查看节点执行状态和数据流转。

  • 加密货币
    支持主流加密货币现货及期货交易所,覆盖交易所全部交易品种数据。

  • 富途证券
    支持港股、美股等多个市场。

    回测注意事项:回测系统目前仅支持富途日线级别数据:

    javascript
    /*backtest start: 2024-05-01 00:00:00 end: 2025-02-17 00:00:00 period: 1d basePeriod: 1d exchanges: [{"eid":"Futures_Futu","currency":"STOCK","fee":[0.03,0.03]}] */ function main() { let info = exchange.SetContractType("TLSA.US") // 设置股票代码:特斯拉 Log("info:", info) // info: {"InstrumentID":"TLSA.US","LotTick":1,"PriceTick":0.01,"VolumeMultiple":1} Log(exchange.GetTicker()) // {"Time":1714482000000,"Symbol":"TLSA.US","Open":0.62,"High":0.63,"Low":0.61,"Sell":0.63,"Buy":0.61,"Last":0.62,"Volume":0,"OpenInterest":0} }

发明者量化交易平台回测系统参数调优功能允许在回测时根据各个参数的调优选项设置参数组合。在「模拟回测」页面的策略参数部分,勾选策略参数右侧的调优选项即可显示调优设置。

  • 最小值:设定参数的起始值。
  • 最大值:设定参数递增变化后的最大值。
  • 步长:参数递增的变化量。
  • 并发线程:
    参数调优时,设置各个回测参数组合并发执行的线程数。该选项仅支持JavaScriptPINEMy语言的策略参数调优,不支持模板参数的调优。

系统根据最小值最大值步长设置生成参数组合,并遍历这些参数组合进行回测(即对每种参数组合都执行一次回测)。策略参数只有类型为**数字型(number)**的参数才能在回测系统中进行参数调优设置。

策略编辑页面的「模拟回测」标签页中(即回测系统),可以设置回测配置、策略参数等选项进行策略回测。回测配置用于设置回测时间范围、回测交易所、交易滑点、手续费等条件;策略参数用于设置策略的参数选项。

当设置好这些参数配置后即可按照设定进行策略回测,那么如何保存这些已设置的配置信息呢?

  • 1、可以使用策略编辑页面的「保存回测设置」按钮,将所有回测配置信息(包含回测设置、策略参数设置)以代码形式记录在策略源码中。
  • 2、在策略编辑页面点击「保存策略」按钮保存策略时,平台会自动记录当前的回测设置、策略参数配置等信息。

回测系统如何加载回测配置?

  • 1、刷新策略编辑页面或重新打开策略编辑页面时,优先自动加载通过「保存回测设置」按钮记录的回测配置信息。
  • 2、如果当前策略代码中没有以注释形式backtest记录的回测配置信息(通过「保存回测设置」按钮保存在策略代码中),回测系统将自动配置回测设置为该策略最后一次点击「保存策略」按钮时的回测配置信息。
  • 3、如果在策略编辑页面中修改了策略代码开头部分以注释形式记录的回测配置信息,需要将更新后的回测配置信息同步到策略回测界面的选项中,可以点击策略编辑区域backtest上方的「回测设置」按钮。

示例

  • 点击「保存回测设置」按钮后,JavaScript/Python/C++/My语言/PINE语言的策略保存回测设置到策略代码时,格式略有差异:

    javascript
    /*backtest start: 2021-06-26 00:00:00 end: 2021-09-23 00:00:00 period: 1d basePeriod: 1h exchanges: [{"eid":"Binance","currency":"BTC_USDT"}] */
    python
    '''backtest start: 2021-06-26 00:00:00 end: 2021-09-23 00:00:00 period: 1d basePeriod: 1h exchanges: [{"eid":"Binance","currency":"BTC_USDT"}] '''
    c++
    /*backtest start: 2021-06-26 00:00:00 end: 2021-09-23 00:00:00 period: 1d basePeriod: 1h exchanges: [{"eid":"Binance","currency":"BTC_USDT"}] */
  • My语言:

    mylang
    (*backtest start: 2021-06-26 00:00:00 end: 2021-09-23 00:00:00 period: 1d basePeriod: 1h exchanges: [{"eid":"Binance","currency":"BTC_USDT"}] *)
  • PINE语言:

    pine
    /*backtest start: 2021-06-26 00:00:00 end: 2021-09-23 00:00:00 period: 1d basePeriod: 1h exchanges: [{"eid":"Binance","currency":"BTC_USDT"}] */

发明者量化交易平台的回测系统支持自定义数据源,回测系统使用GET方法请求自定义的URL(可公开访问的网址)来获取外部数据源进行回测,附加的请求参数如下:

参数意义说明
symbol品种名称现货行情数据示例:BTC_USDT,期货行情数据示例:BTC_USDT.swap,期货永续合约资金费率数据示例:BTC_USDT.funding,期货永续合约价格指数数据示例:BTC_USDT.index
eid交易所例如:OKX、Futures_OKX
round数据精度为true时,表示由自定义数据源返回的数据中定义具体精度。发明者量化交易平台回测系统向自定义数据源发送的请求固定为:round=true
periodK线数据的周期(毫秒)例如:60000表示1分钟周期
depth深度档数1-20
trades是否需要逐笔成交数据真(1)/假(0)
from开始时间unix时间戳
to结束时间unix时间戳
detail请求品种的详细信息为true时,表示需要由自定义数据源提供。发明者量化交易平台回测系统向自定义数据源发送的请求固定为:detail=true
custom--可忽略此参数

现货交易所、期货交易所对象的数据源设置为自定义数据源(feeder)时,回测系统向自定义数据源服务发送请求的示例:

url
http://customserver:9090/data?custom=0&depth=20&detail=true&eid=Bitget&from=1351641600&period=86400000&round=true&symbol=BTC_USDT&to=1611244800&trades=1 http://customserver:9090/data?custom=0&depth=20&detail=true&eid=Futures_OKX&from=1351641600&period=86400000&round=true&symbol=BTC_USDT.swap&to=1611244800&trades=1

返回的格式必须为以下两种格式之一(系统自动识别):

  • 模拟级Tick,以下是JSON数据示例:
    json
    { "detail": { "eid": "Binance", "symbol": "BTC_USDT", "alias": "BTCUSDT", "baseCurrency": "BTC", "quoteCurrency": "USDT", "marginCurrency": "USDT", "basePrecision": 5, "quotePrecision": 2, "minQty": 0.00001, "maxQty": 9000, "minNotional": 5, "maxNotional": 9000000, "priceTick": 0.01, "volumeTick": 0.00001, "marginLevel": 10 }, "schema":["time", "open", "high", "low", "close", "vol"], "data":[ [1564315200000, 9531300, 9531300, 9497060, 9497060, 787], [1564316100000, 9495160, 9495160, 9474260, 9489460, 338] ] }
  • 实盘级Tick,以下是JSON数据示例:
    Tick级回测数据(包含盘口深度信息,深度格式为[价格, 数量]的数组。可包含多级深度,asks按价格升序排列,bids按价格降序排列)。
    json
    { "detail": { "eid": "Binance", "symbol": "BTC_USDT", "alias": "BTCUSDT", "baseCurrency": "BTC", "quoteCurrency": "USDT", "marginCurrency": "USDT", "basePrecision": 5, "quotePrecision": 2, "minQty": 0.00001, "maxQty": 9000, "minNotional": 5, "maxNotional": 9000000, "priceTick": 0.01, "volumeTick": 0.00001, "marginLevel": 10 }, "schema":["time", "asks", "bids", "trades", "close", "vol"], "data":[ [1564315200000, [[9531300, 10]], [[9531300, 10]], [[1564315200000, 0, 9531300, 10]], 9497060, 787], [1564316100000, [[9531300, 10]], [[9531300, 10]], [[1564316100000, 0, 9531300, 10]], 9497060, 787] ] }
字段说明
detail请求数据的品种详细信息,包含计价币名称、交易币名称、精度、最小下单量等
schema指定data数组中列的属性,区分大小写。仅限于 time, open, high, low, close, vol, asks, bids, trades
data按照schema设置的列结构记录的数据

detail字段

字段说明
eid交易所ID,注意同一交易所的现货与期货使用不同的eid
symbol交易品种代码
alias当前交易品种代码在交易所中对应的symbol
baseCurrency交易币种
quoteCurrency计价币种
marginCurrency保证金币种
basePrecision交易币种精度
quotePrecision计价币种精度
minQty最小下单量
maxQty最大下单量
minNotional最小下单金额
maxNotional最大下单金额
priceTick价格最小变动单位
volumeTick下单量最小变动单位
marginLevel期货杠杆倍数
contractType对于永续合约设置为:swap,回测系统将继续发送资金费率、价格指数请求

特殊列属性asksbidstrades说明:

字段说明备注
asks / bids[[价格, 数量], ...]例如实盘级 Tick数据示例中的数据:[[9531300, 10]]
trades[[时间, 方向(0:买,1:卖), 价格, 数量], ...]例如实盘级 Tick数据示例中的数据:[[1564315200000, 0, 9531300, 10]]

期货交易所的永续合约回测时,自定义数据源还需要提供额外的资金费率数据和价格指数数据。只有当请求的行情数据返回时,返回结构中的detail字段包含"contractType": "swap"键值对,回测系统才会继续发送资金费率请求。
当回测系统收到资金费率数据后,才会继续发送价格指数数据请求。

资金费率数据结构如下:

json
{ "detail": { "eid": "Futures_Binance", "symbol": "BTC_USDT.funding", "alias": "BTC_USDT.funding", "baseCurrency": "BTC", "quoteCurrency": "USDT", "marginCurrency": "", "basePrecision": 8, "quotePrecision": 8, "minQty": 1, "maxQty": 10000, "minNotional": 1, "maxNotional": 100000000, "priceTick": 1e-8, "volumeTick": 1e-8, "marginLevel": 10 }, "schema": [ "time", "open", "high", "low", "close", "vol" ], "data": [ [ 1584921600000, -16795, -16795, -16795, -16795, 0 ], [ 1584950400000, -16294, -16294, -16294, -16294, 0 ] // ... ] }
  • 相邻周期间隔为8小时
  • 例如币安资金费率每8小时更新一次,资金费率数据为何是 -16795?
    这是因为与K线数据一样,为避免网络传输过程中浮点数精度丢失,数据采用整型表示;资金费率数据也可能为负值。

回测系统发出的资金费率数据请求示例:

url
http://customserver:9090/data?custom=0&depth=20&detail=true&eid=Futures_Binance&from=1351641600&period=86400000&round=true&symbol=BTC_USDT.funding&to=1611244800&trades=0

价格指数数据结构如下:

json
{ "detail": { "eid": "Futures_Binance", "symbol": "BTC_USDT.index", "alias": "BTCUSDT", "baseCurrency": "BTC", "quoteCurrency": "USDT", "contractType": "index", "marginCurrency": "USDT", "basePrecision": 3, "quotePrecision": 1, "minQty": 0.001, "maxQty": 1000, "minNotional": 0, "maxNotional": 1.7976931348623157e+308, "priceTick": 0.1, "volumeTick": 0.001, "marginLevel": 10, "volumeMultiple": 1 }, "schema": [ "time", "open", "high", "low", "close", "vol" ], "data": [ [1584921600000, 58172, 59167, 56902, 58962, 0], [1584922500000, 58975, 59428, 58581, 59154, 0], // ... ] }

回测系统发出的价格指数数据请求示例:

url
http://customserver:9090/data?custom=0&depth=20&detail=true&eid=Futures_Binance&from=1351641600&period=86400000&round=true&symbol=BTC_USDT.index&to=1611244800&trades=0

指定数据源地址,例如:http://120.24.2.20:9090/data。自定义数据源服务程序使用Golang编写:

golang
package main import ( "fmt" "net/http" "encoding/json" ) func Handle (w http.ResponseWriter, r *http.Request) { // e.g. set on backtest DataSourse: http://xxx.xx.x.xx:9090/data // request: GET http://xxx.xx.x.xx:9090/data?custom=0&depth=20&detail=true&eid=OKX&from=1584921600&period=86400000&round=true&symbol=BTC_USDT&to=1611244800&trades=1 // http://xxx.xx.x.xx:9090/data?custom=0&depth=20&detail=true&eid=Futures_Binance&from=1599958800&period=3600000&round=true&symbol=BTC_USDT.swap&to=1611244800&trades=0 fmt.Println("request:", r) // response defer func() { // response data /* e.g. data { "detail": { "eid": "Binance", "symbol": "BTC_USDT", "alias": "BTCUSDT", "baseCurrency": "BTC", "quoteCurrency": "USDT", "marginCurrency": "USDT", "basePrecision": 5, "quotePrecision": 2, "minQty": 0.00001, "maxQty": 9000, "minNotional": 5, "maxNotional": 9000000, "priceTick": 0.01, "volumeTick": 0.00001, "marginLevel": 10 }, "schema": [ "time", "open", "high", "low", "close", "vol" ], "data": [ [1610755200000, 3673743, 3795000, 3535780, 3599498, 8634843151], [1610841600000, 3599498, 3685250, 3385000, 3582861, 8015772738], [1610928000000, 3582499, 3746983, 3480000, 3663127, 7069811875], [1611014400000, 3662246, 3785000, 3584406, 3589149, 7961130777], [1611100800000, 3590194, 3641531, 3340000, 3546823, 8936842292], [1611187200000, 3546823, 3560000, 3007100, 3085013, 13500407666], [1611273600000, 3085199, 3382653, 2885000, 3294517, 14297168405], [1611360000000, 3295000, 3345600, 3139016, 3207800, 6459528768], [1611446400000, 3207800, 3307100, 3090000, 3225990, 5797803797], [1611532800000, 3225945, 3487500, 3191000, 3225420, 8849922692] ] } */ // /* 模拟级Tick ret := map[string]interface{}{ "detail": map[string]interface{}{ "eid": "Binance", "symbol": "BTC_USDT", "alias": "BTCUSDT", "baseCurrency": "BTC", "quoteCurrency": "USDT", "marginCurrency": "USDT", "basePrecision": 5, "quotePrecision": 2, "minQty": 0.00001, "maxQty": 9000, "minNotional": 5, "maxNotional": 9000000, "priceTick": 0.01, "volumeTick": 0.00001, "marginLevel": 10, }, "schema": []string{"time","open","high","low","close","vol"}, "data": []interface{}{ []int64{1610755200000, 3673743, 3795000, 3535780, 3599498, 8634843151}, // 1610755200000 : 2021-01-16 08:00:00 []int64{1610841600000, 3599498, 3685250, 3385000, 3582861, 8015772738}, // 1610841600000 : 2021-01-17 08:00:00 []int64{1610928000000, 3582499, 3746983, 3480000, 3663127, 7069811875}, []int64{1611014400000, 3662246, 3785000, 3584406, 3589149, 7961130777}, []int64{1611100800000, 3590194, 3641531, 3340000, 3546823, 8936842292}, []int64{1611187200000, 3546823, 3560000, 3007100, 3085013, 13500407666}, []int64{1611273600000, 3085199, 3382653, 2885000, 3294517, 14297168405}, []int64{1611360000000, 3295000, 3345600, 3139016, 3207800, 6459528768}, []int64{1611446400000, 3207800, 3307100, 3090000, 3225990, 5797803797}, []int64{1611532800000, 3225945, 3487500, 3191000, 3225420, 8849922692}, }, } // */ /* 实盘级Tick ret := map[string]interface{}{ "detail": map[string]interface{}{ "eid": "Binance", "symbol": "BTC_USDT", "alias": "BTCUSDT", "baseCurrency": "BTC", "quoteCurrency": "USDT", "marginCurrency": "USDT", "basePrecision": 5, "quotePrecision": 2, "minQty": 0.00001, "maxQty": 9000, "minNotional": 5, "maxNotional": 9000000, "priceTick": 0.01, "volumeTick": 0.00001, "marginLevel": 10, }, "schema": []string{"time", "asks", "bids", "trades", "close", "vol"}, "data": []interface{}{ []interface{}{1610755200000, []interface{}{[]int64{9531300, 10}}, []interface{}{[]int64{9531300, 10}}, []interface{}{[]int64{1610755200000, 0, 9531300, 10}}, 9497060, 787}, []interface{}{1610841600000, []interface{}{[]int64{9531300, 15}}, []interface{}{[]int64{9531300, 15}}, []interface{}{[]int64{1610841600000, 0, 9531300, 11}}, 9497061, 789}, }, } */ b, _ := json.Marshal(ret) w.Write(b) }() } func main () { fmt.Println("listen http://localhost:9090") http.HandleFunc("/data", Handle) http.ListenAndServe(":9090", nil) }

测试策略,JavaScript示例:

javascript
/*backtest start: 2021-01-16 08:00:00 end: 2021-01-22 00:00:00 period: 1d basePeriod: 1d exchanges: [{"eid":"OKX","currency":"BTC_USDT","feeder":"http://120.24.2.20:9090/data"}] args: [["number",2]] */ function main() { var ticker = exchange.GetTicker() var records = exchange.GetRecords() Log(exchange.GetName(), exchange.GetCurrency()) Log(ticker) Log(records) }

发明者量化交易平台开源了JavaScript语言和Python语言的本地回测引擎,支持回测时设置底层K线周期。

以Python语言为例,简要说明本地回测引擎的使用方法:

python
'''backtest start: 2022-02-19 00:00:00 end: 2022-03-22 12:00:00 period: 15m exchanges: [{"eid":"Binance","currency":"BTC_USDT","balance":10000,"stocks":0}] ''' # Part 1 ----------------------------------- # 初始化回测引擎,backtest 为回测引擎配置信息,与 FMZ 平台线上回测系统配置保持一致 # 通过 __doc__ 读取上方的配置字符串并初始化回测环境 from fmz import * task = VCtx(__doc__) # initialize backtest engine from __doc__ # End ----------------------------------- # Part 2 ----------------------------------- # 以下为待测试的策略代码示例(可以从 FMZ 平台复制完整的策略代码) # 注意:仅复制策略代码时不包含参数设计、交互设计等其他配置内容 def onTick(): ticker = _C(exchange.GetTicker) LogStatus(_D(), ticker.Last) def main(): exchange.SetCurrency("ETH_USDT") # exchange.SetContractType("swap") # 如果测试期货交易所对象,需要设置合约,例如这里设置为永续合约 Log(exchange.GetAccount()) while True: onTick() Sleep(1000) # End ----------------------------------- # Part 3 ----------------------------------- # 执行回测并捕获结束信号,回测结束时会触发 EOF 异常 # 捕获异常后可以输出回测结果数据或展示回测图表 try: main() except: print("Strategy testing completed.") print(task.Join(False)) # print backtest result # task.Show() # or show backtest chart # End -----------------------------------

  • 策略编辑页面和策略回测页面切换的快捷键
    使用Ctrl + ,键切换回测页面和策略编辑页面,按住Ctrl键后,单按,键。
  • 策略保存的快捷键
    使用Ctrl + s键保存策略。
  • 启动回测的快捷键
    使用Ctrl + b键启动回测。

  • 回测系统日志数据下载
    打开具体策略,切换到「回测页面」进行策略回测。回测结束后,在显示的「状态信息」栏右上角有「下载表格」按钮,点击即可下载回测结束时状态栏数据的CSV格式文件。
  • 回测系统状态栏数据下载
    打开具体策略,切换到「回测页面」进行策略回测。回测结束后,在显示的「日志信息」栏右上角有「下载表格」按钮,点击即可下载回测日志数据的CSV格式文件。

回测系统夏普比率算法源码:

javascript
function returnAnalyze(totalAssets, profits, ts, te, period, yearDays) { // force by days period = 86400000 if (profits.length == 0) { return null } var freeProfit = 0.03 // 0.04 var yearRange = yearDays * 86400000 var totalReturns = profits[profits.length - 1][1] / totalAssets var annualizedReturns = (totalReturns * yearRange) / (te - ts) // MaxDrawDown var maxDrawdown = 0 var maxAssets = totalAssets var maxAssetsTime = 0 var maxDrawdownTime = 0 var maxDrawdownStartTime = 0 var winningRate = 0 var winningResult = 0 for (var i = 0; i < profits.length; i++) { if (i == 0) { if (profits[i][1] > 0) { winningResult++ } } else { if (profits[i][1] > profits[i - 1][1]) { winningResult++ } } if ((profits[i][1] + totalAssets) > maxAssets) { maxAssets = profits[i][1] + totalAssets maxAssetsTime = profits[i][0] } if (maxAssets > 0) { var drawDown = 1 - (profits[i][1] + totalAssets) / maxAssets if (drawDown > maxDrawdown) { maxDrawdown = drawDown maxDrawdownTime = profits[i][0] maxDrawdownStartTime = maxAssetsTime } } } if (profits.length > 0) { winningRate = winningResult / profits.length } // trim profits var i = 0 var datas = [] var sum = 0 var preProfit = 0 var perRatio = 0 var rangeEnd = te if ((te - ts) % period > 0) { rangeEnd = (parseInt(te / period) + 1) * period } for (var n = ts; n < rangeEnd; n += period) { var dayProfit = 0.0 var cut = n + period while (i < profits.length && profits[i][0] < cut) { dayProfit += (profits[i][1] - preProfit) preProfit = profits[i][1] i++ } perRatio = ((dayProfit / totalAssets) * yearRange) / period sum += perRatio datas.push(perRatio) } var sharpeRatio = 0 var volatility = 0 if (datas.length > 0) { var avg = sum / datas.length; var std = 0; for (i = 0; i < datas.length; i++) { std += Math.pow(datas[i] - avg, 2); } volatility = Math.sqrt(std / datas.length); if (volatility !== 0) { sharpeRatio = (annualizedReturns - freeProfit) / volatility } } return { totalAssets: totalAssets, yearDays: yearDays, totalReturns: totalReturns, annualizedReturns: annualizedReturns, sharpeRatio: sharpeRatio, volatility: volatility, maxDrawdown: maxDrawdown, maxDrawdownTime: maxDrawdownTime, maxAssetsTime: maxAssetsTime, maxDrawdownStartTime: maxDrawdownStartTime, winningRate: winningRate } }