The resource loading... loading...

Backtest System

After you have accomplished the design of a quantitative trading strategy, how can you know the basic situation of your strategy, such as the logic of the strategy and the direction of the strategy’s returns? Of course, we cannot use real money directly to run the strategy on the real trading market, but we can use historical data to test your strategy and know the profits of your strategy in the historical data.

Backtest System Model

FMZ Quant Trading Platform divides the backtest system into bot level and simulation level. The bot level is to backtest completely according to the complete historical data; while simulation level backtest generates tick data according to the real K-line data at regular intervals for backtest. They are both based on the real historical data, but the bot level data is more accurate and the results are more credible. However, backtesting is just the performance of the strategy according to historical data. The historical data cannot fully represent the future market. The historical market may repeat, or it may also lead to the Black Swan. Therefore, the backtest results should be treated rationally and objectively.

The simulation level Tick generates the simulated tick data based on the underlying K-line period, each underlying K-line period will generate a maximum of 12 backtest time points; While the real market level Tick backtesting uses real collected second-by-second tick data, the volume of data is very large and the backtesting speed is slow, so it cannot be backtested for a very long period of time. The backtest mechanism of FMZ Quant allows the strategy to trade multiple times on a single K-line, avoiding the situation where the trading can only be executed at the closing price. It is more accurate while taking into account the speed of backtest.

Backtesting system mechanism description

  • Simulation Level Tick The simulation level Tick is based on the underlying K-line data of the backtest system, simulating tick data to backtest within the framework of the highest price, lowest price, opening price, and closing price values of a given underlying K-line bar according to a certain algorithm. As real-time tick data on the backtesting time series, it returns when the strategy program calls the interface. For details, please refer to: Backtesting System Simulation Level Mechanism Description.

  • Bot Level Tick The bot level backtest is the actual tick level data in the Bar time series. For strategies based on tick level data, using real market level to backtest is closer to reality. In bot level backtest, tick data is real recorded data, not simulated one. It supports depth data, record data playback of market tradings, custom depth and each individual trading data. The maximum size of the real-market-level data backtest is up to a maximum of 50MB, with no limit on the backtest time range within the upper limit of the dataset. If you need to enlarge the backtest time range as much as possible, you can reduce the value of the depth gear setting and do not use each individual trading data to increase the backtest time range. Call GetDepth, GetTrades functions to obtain playback market data. At a moment of market data on the timeline, calling GetTicker, GetTrades, GetDepth and GetRecords will not push the time multiple times when the time moves on the backtest timeline (which will not trigger a jump to the next market data moment). Repeated calls to one of the above functions will push the backtest time to move on the backtest timeline (jump to the next market data moment). When the real market level is used for backtest, an earlier time is not recommended to choose. There may be no real-market-level data in the premature time period.

Bot-level Tick and Simulation-level Tick modes, the transaction matching mechanism of the backtest system: order transaction matching is carried out according to the price seen and the full volume is traded. Therefore, the scenario of partial transaction cannot be tested in the backtest system.

The Backtesting System Supports Multiple Programming Languages

The backtesting system supports backtesting strategies written and designed by: JavaScript, TypeScript, Python, C++, PINE, MyLanguage, Blockly visualization. The backtest of JavaScript and C++ trading strategies is conducted in the browser, and the real market bot or WexApp emulated exchange real market (i.e. the WexApp emulated exchange of FMZ Quant Trading platform) runs without installing any other software, libraries or modules. The backtest of Python is performed on the docker; it can be performed on the public server added by FMZ Quant Trading platform, and it can also be performed on user’s own docker. The real market operation and backtest both rely on the Python installed on the system where the docker is located. If some libraries are needed, they need to be installed manually (only common Python libraries are supported on FMZ Quant public servers).

It supports JavaScript strategy backtesting debugging in Chrome DevTools, please refer to.

Exchanges Supported in the Backtesting System

  • Cryptocurrency Mainstream spot and futures exchanges for cryptocurrencies; supports data on all types of exchanges.
  • Futu Securities Hong Kong stocks, U.S. stocks and other markets.

Backtesting System Parameter Optimization

The parameter optimization function of the backtest system of FMZ Quant Trading Platform is to set the parameter combinations according to every parameter optimization option during backtesting. In the strategy parameter section of the “Simulation Backtesting” page, check the Optimization option on the right side of the strategy parameter to display the optimization settings.

  • Minimum value: to limit the start value of the parameters.
  • Maximum value: to limit the maximum value of the parameters after incremental changes.
  • Step size: the incremental variable amount of the parameters.
  • Concurrent threads: During parameter optimization, set the number of threads for concurrent execution of each backtest parameter combination. This option only supports strategy parameter optimization in JavaScript, PINE, and My Language, and does not support parameter optimization on templates.

Parameter combinations are generated based on the minimum, maximum, and step size settings. The backtesting system iterates through these parameter combinations for backtesting (i.e., backtesting each parameters combination once). Only strategy parameters of number type can be optimized in the backtesting system.

Save Backtest Settings

In the strategy editing page, in the pagination of “Backtest” (namely the backtest system), you can set options like backtest configurations and strategy parameters to backtest the strategy. Backtest settings refer to backtest time range, exchange platform, slippoint and service fee etc.; while strategy parameters are used to set parameter options for strategies. When these parameters are set, you can follow the set backtesting strategy, then how to save the set configuration information?

    1. You can use the “Save Backtest Settings” button on the Strategy Editing Page to record all the backtest configuration information (including backtest settings and strategy parameter settings) in the strategy source code in the form of code.
    1. When you save a strategy by clicking the “Save Strategy” button on the strategy editing page, the platform will automatically record the current backtest settings, strategy parameter configurations, and other information. How to load backtest configuration into the backtest system?
    1. When refreshing the strategy editing page or reopening this strategy editing page, the backtest configuration information recorded by the “Save Backtest Settings” button will be automatically loaded first.
    1. If there is no backtest configuration information recorded in the current strategy code as a comment backtest (saved in the strategy code via the “Save Backtest Settings” button), the backtest system automatically configures the backtest settings to the backtest configuration information when the “Save Strategy” button was last clicked for the current strategy.
    1. If the backtest configuration information recorded in the form of comments at the beginning of the strategy code is modified on the strategy editing page, you need to synchronize the currently updated backtest configuration information to the strategy backtest interface option. You can click the “Backtest Settings” button above the backtest in the strategy editing area.
/*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"}]
*/
'''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"}]
'''
/*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"}]
*/

Click “Save backtest settings”, there are slight format differences on JavaScript/Python/C++/MyLanguage/PINE languages when saving backtest settings to the strategy code: MyLanguage:

(*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 Language:

/*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"}]
*/

Custom Data Source

The backtesting system of the FMZ Quant Trading Platform supports custom data sources, the backtesting system uses the GET method to request a custom URL (publicly accessible URL) to obtain an external data source for backtest. The additional request parameters are as follows:

Parameter Meaning Explanation
symbol Symbol Name Spot market data, such as: BTC_USDT, futures market data, such as: BTC_USDT.swap, futures perpetual contract funding rate data, such as: BTC_USDT.funding, futures perpetual contract price index data, such as: BTC_USDT.index
eid Exchanges such as OKX, Futures_OKX
round Data Accuracy True means that the specific precision is defined in the data fed back by the custom data source. The request sent by the FMZ Quant Trading Platform Backtesting System to the custom data source is fixed as: round=true
period K-line Data Period (Milliseconds) such as: 60000 is a 1-minute period
depth Depth Levels 1-20
trades Whether Need to Split Data true(1) / false(0)
from Start Time unix timestamp
to End Time unix timestamp
detail Request data for symbol details True means that it needs to be provided by a custom data source. The request sent by the FMZ Quant Trading Platform Backtesting System to the custom data source is fixed as: detail=true
custom This parameter can be ignored

When the data source of the spot exchange and futures exchange objects is set to a custom data source (feeder), the backtesting system sends a request to the custom data source service:

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

Data Format

The returned format must be one of the following two formats (which will be recognized by the system automatically):

  • Simulation level Tick, the following is an example of JSON 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":[
            [1564315200000, 9531300, 9531300, 9497060, 9497060, 787],
            [1564316100000, 9495160, 9495160, 9474260, 9489460, 338]
        ]
    }
    
  • Bot level tick, the following is an example of JSON data: Tick-level backtest data (contains information on the depth of the market, and the depth format is an array of [price, volume]. It can have multiple levels of depth, asks for price ascending order, bids for price descending order).

    {
        "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]
        ]
    }
    
Field Description
detail Detailed information on the requested data type,

including the name of the denominated currency, the name of the trading currency, the precision, the minimum order quantity, etc. | | schema | It specifies the attributes of the columns in the data array, which is case sensitive and is only limited to time, open, high, low, close, vol, asks, bids, trades| | data | The column structure, recorded data according to the schema settings.|

detail field

Field Description
eid Exchange Id, please note that the spot and futures of a
certain exchange have different eids.
symbol Trading product code
alias The symbol in the exchange corresponding to the current
trading product code
baseCurrency Trading Currency
quoteCurrency Denominated Currency
marginCurrency Margin Currency
basePrecision Transaction Currency Accuracy
quotePrecision Pricing Currency Accuracy
minQty Minimum Order Quantity
maxQty Maximum Order Quantity
minNotional Minimum Order Amount
maxNotional Maximum Order Amount
priceTick Price Jump
volumeTick Minimum change value of order quantity (one jump in
order quantity)
marginLevel Futures Leverage Value
contractType For perpetual contracts set to: swap, the

backtest system will continue to send funding rate and price index requests |

Special column attributes asks, bids, trades:

Field Description Remarks
asks / bids [[price, volume], …] For example, the data in

the Live Trading Level Tick data example: [[9531300, 10]] | | trades | [[time,direction(0:buy,1:sell),price,volume], …] | For example, the data in the Live Trading Level Tick data example: [[1564315200000, 0, 9531300, 10]] |

When backtesting perpetual contracts on futures exchanges, custom data sources also require additional funding rate data and price index data. The backtesting system will continue to send requests for funding rates only when the requested market data is returned and the detail field in the returned structure contains the "contractType": "swap" key-value pair.

When the backtesting system receives funding rate data, it will continue to send requests for price index data.

The funding rate data structure is as follows:

{
    "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
        ]
        // ...
    ]
}
  • The interval between adjacent periods is 8 hours
  • For example, Binance’s funding rate is updated every 8 hours. Why is the funding rate data -16795? Because like K-line data, in order to avoid the loss of floating point precision during network transmission, the data uses integer type; the funding rate data may also be negative.

An example of a funding rate data request from the backtesting system is:

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

The price index data structure is as follows:


{
    "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],
        // ...
    ]
}

An example of a price index data request sent by the backtesting system is:

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

Example for Custom Data Source

Specify the data source address, e.g., http://120.24.2.20:9090/data. The custom data source service program is written using 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]
            ]
        }
        */
        
        // /* Simulation level 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},
            },
        }
        // */

        /* Bot level 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)
}

Test strategy, JavaScript example:

/*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)
}

Local Backtest Engine

FMZ Quant Trading Platform has open-sourced for the JavaScript language and the Python language of the local backtest engine, supporting setting Underlying K-line Period during backtesting.

Backtest Page Shortcut Keys

  • Shortcut key for switching between strategy “Editing” page and the “Backtesting” page Use the key Ctrl +, to switch back to “Backtest” page and “Edit Strategy” page. After the key Ctrl, press the key ,.

  • Shortcut key for saving strategy Use the key Ctrl + s to save strategies.

  • Shortcut for starting strategy backtest Use the key Ctrl + b to enable “Start Backtest”.

Backtest Data Download

  • Backtesting system log data download Open the specific strategy and switch to the “Backtest Page” to backtest the strategy. In the “Status Information” column of the strategy displayed after the backtest, there is a “Download Table” button in the upper right corner. Click it to download a CSV file of the status column data at the end of the backtest.
  • Backtesting system status bar data download Open the specific strategy and switch to the “Backtest Page” to backtest the strategy. In the “Log Information” column of the strategy displayed after the backtest, there is a “Download Table” button in the upper right corner. Click it to download the CSV format file of the backtesting log data.

Sharpe Algorithm in Backtesting System

The source code of Sharpe algorithm in backtesting system:

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
    }
}
Strategy Editor Strategy Entry Functions