Type/to search
Welcome to FMZ Quant Trading Platform
Programming Languages
JavaScript
TypeScript
Python
C++
MyLanguage
PINE Language
Blockly Visual Programming
Workflow
Key Security
Live Trading
Strategy Library
Docker
Deploy Docker
One-Click Docker Rental
Manual Deployment of Bot
Docker Operation Precautions
Global IP Address Specification
Command Line Parameters for Bot Program
Live Trading Data Migration
Docker Monitor
Exchange
Strategy Editor
Backtesting System
Strategy Entry Functions
Strategy Framework and API Functions
Template Library
Strategy Parameters
Interactive Controls
Options Trading
C++ Strategy Writing Guide
JavaScript Strategy Writing Guide
Web3
Built-in Libraries
Extended API Interface
MCP Service
Trading Terminal
Data Explorer
Alpha Factor Analysis Tool
General Protocol
Debugging Tool
Remote Editing
Import and Export of Complete Strategies
Multi-language Support
Live Trading and Strategy Grouping
Live Trading Display
Strategy Sharing and Renting
Live Trading Message Push
Common Causes of Live Trading Errors and Abnormal Exits
Exchange-Specific Notes

After completing the design of your quantitative trading strategy, how do you verify key metrics such as the strategy's logical correctness and expected returns? Obviously, you cannot test directly in the market with real funds. The correct approach is to backtest the strategy using historical data, evaluating its profitability and risk characteristics by analyzing the strategy's performance in historical market conditions.

The FMZ Quant Trading Platform divides backtesting modes into Real-tick Level backtesting and Simulated-tick Level backtesting. Real-tick Level backtesting is completely based on complete historical data for backtesting; Simulated-tick Level backtesting generates tick data based on real candlestick data for backtesting. Both are based on real historical data for backtesting, but Real-tick Level backtesting has more accurate data and more reliable results. It should be noted that backtesting only reflects the performance of strategies under historical data, and historical data cannot fully represent future market conditions, so backtesting results should be treated with a rational and objective attitude.

Simulated-tick Level backtesting generates simulated tick data based on the underlying candlestick period, generating up to 12 backtesting time points on each underlying candlestick period. Real-tick Level backtesting uses real collected second-by-second tick data, which has a large amount of data and slower backtesting speed, so it is not suitable for backtesting particularly long time ranges. FMZ Quant's backtesting mechanism allows strategies to trade multiple times on a single candlestick, avoiding the limitation of only being able to trade at the closing price, ensuring accuracy while balancing backtesting speed.

Backtesting System Mechanism Description

  • Simulated-tick Level
    Simulated-tick Level backtesting generates tick data for backtesting based on the underlying candlestick data of the backtesting system, simulating tick data within the price framework composed of the high, low, open, and close prices of the given underlying candlestick bar according to specific algorithms, serving as real-time tick data on the backtesting time series, which is returned when the strategy program calls the interface. For details, please refer to: Backtesting System Simulated Level Mechanism Description.

  • Real-tick Level
    Real-tick level backtesting uses real tick-level data in the bar time series. For strategies based on tick-level data, using real-tick level backtesting is closer to actual conditions. The ticks in real-tick level backtesting are real recorded data, not simulated. It supports depth data, market trade record data playback, supports custom depth, and supports trade-by-trade data. Real-tick level backtesting data supports a maximum of 50MB, with no limit on the backtesting time range within the data limit. To maximize the backtesting time range as much as possible, you can reduce the depth level value settings and not use trade-by-trade data to extend the backtesting time range. Call GetDepth and GetTrades functions to obtain playback market data. At a certain market data moment on the timeline, calling GetTicker, GetTrades, GetDepth, GetRecords will not move time forward multiple times on the backtesting timeline (will not trigger jumping to the next market data moment). Repeated calls to any of the above functions will push the backtesting time forward on the backtesting timeline (jump to the next market data moment). When using real-tick level backtesting, it is not advisable to select too early a time, as earlier time periods may not have real-tick level data.

Real-tick Level and Simulated-tick Level mode backtesting system order matching mechanism: Order matching is executed based on price-taking and full-fill execution. Therefore, partial fill scenarios cannot be tested in the backtesting system.

The following test code will exhibit different performance for different data granularities (A. Live trading level backtest, B. Simulation level backtest (smaller underlying K-line period), C. Simulation level backtest (larger underlying K-line period), etc.). Both the number of trades and profit/loss results will vary. When backtesting, the smallest possible data granularity should be maintained. While backtesting may be faster with larger data granularity, the results obtained may lack objectivity.

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) { // Price rises above threshold -> Go short exchange.Sell(ticker.Last, lotSize) Log("Short @", ticker.Last) direction = "short" } else if ((!direction || direction == "short") && diff <= -delta) { // Price falls below threshold -> Go long exchange.Buy(ticker.Last, lotSize) Log("Long @", ticker.Last) direction = "long" } // Should be as short as possible in Tick mode, no impact in K-line backtest Sleep(100) } }

The backtesting system supports: JavaScript, TypeScript, Python, C++, PINE, My Language, Blockly visual programming, and Workflow workflow-designed strategies for backtesting.

  1. JavaScript and C++ strategy backtesting runs in the browser. Strategies written in JavaScript and C++ do not require installing any additional software, libraries, or modules for live trading or backtesting.

  2. Python strategy backtesting runs on the docker. You can backtest on FMZ Quant's public servers or on your own docker. Both live trading and backtesting depend on the Python environment installed on the system where the docker is located. If specific libraries are needed, they must be installed manually. FMZ Quant's public servers only support commonly used Python libraries.

  3. JavaScript strategies support debugging in Chrome browser DevTools during backtesting. Reference documentation.

  4. Workflow strategies support backtesting, with visual display of node execution status and data flow.

  • Cryptocurrency
    Supports mainstream cryptocurrency spot and futures exchanges, covering all trading pairs data from exchanges.

  • Futu Securities
    Supports multiple markets including Hong Kong stocks and US stocks.

    Backtesting Notes: The backtesting system currently only supports Futu daily level data:

    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") // Set stock symbol: Tesla 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} }

The FMZ Quant Trading Platform backtest system parameter optimization feature allows you to set parameter combinations based on optimization options for each parameter during backtesting. On the "Simulation Backtest" page in the strategy parameters section, check the Optimization option on the right side of the strategy parameter to display optimization settings.

  • Minimum Value: Set the starting value of the parameter.
  • Maximum Value: Set the maximum value after the parameter increments.
  • Step Size: The increment amount for parameter changes.
  • Concurrent Threads:
    When optimizing parameters, set the number of threads for concurrent execution of each backtest parameter combination. This option only supports parameter optimization for JavaScript, PINE, and MyLanguage strategies, and does not support template parameter optimization.

The system generates parameter combinations based on Minimum Value, Maximum Value, and Step Size settings, and iterates through these parameter combinations for backtesting (i.e., executes one backtest for each parameter combination). Only strategy parameters of type number can be configured for parameter optimization in the backtest system.

In the "Simulation Backtest" tab of the Strategy Editor Page (i.e., the backtesting system), you can configure backtest settings, strategy parameters, and other options for strategy backtesting. Backtest configuration is used to set the backtest time range, backtest exchange, trading slippage, commission fees, and other conditions; strategy parameters are used to set the parameter options of the strategy.

After setting these parameter configurations, you can perform strategy backtesting according to the settings. So how do you save these configured settings?

    1. You can use the "Save Backtest Settings" button on the Strategy Editor Page to record all backtest configuration information (including backtest settings and strategy parameter settings) in the strategy source code in the form of code.
    1. When clicking the "Save Strategy" button on the strategy editor page to save the strategy, the platform will automatically record the current backtest settings, strategy parameter configurations, and other information.

How does the backtesting system load backtest configurations?

    1. When refreshing the strategy editor page or reopening the strategy editor page, it will prioritize automatically loading the backtest configuration information recorded through the "Save Backtest Settings" button.
    1. If there is no backtest configuration information recorded in comment form backtest in the current strategy code (saved in the strategy code through the "Save Backtest Settings" button), the backtesting system will automatically configure the backtest settings to the backtest configuration information from the last time the "Save Strategy" button was clicked for this strategy.
    1. If you have modified the backtest configuration information recorded in comment form at the beginning of the strategy code in the strategy editor page, and need to synchronize the updated backtest configuration information to the options in the strategy backtest interface, you can click the "Backtest Settings" button above backtest in the strategy editing area.

Examples

  • After clicking the "Save Backtest Settings" button, there are slight differences in the format when saving backtest settings to the strategy code for JavaScript/Python/C++/MyLanguage/PINE language strategies:

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

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

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

The FMZ Quant Trading Platform's backtesting system supports custom data sources. The backtesting system uses the GET method to request custom URLs (publicly accessible addresses) to obtain external data sources for backtesting. The additional request parameters are as follows:

ParameterMeaningDescription
symbolSymbol nameSpot market data example: BTC_USDT, Futures market data example: BTC_USDT.swap, Perpetual futures funding rate data example: BTC_USDT.funding, Perpetual futures price index data example: BTC_USDT.index
eidExchangeFor example: OKX, Futures_OKX
roundData precisionWhen true, indicates that the data returned by the custom data source defines specific precision. The request sent by the FMZ Quant Trading Platform backtesting system to the custom data source is fixed as: round=true
periodK-line data period (milliseconds)For example: 60000 represents a 1-minute period
depthOrder book depth levels1-20
tradesWhether tick data is requiredTrue (1) / False (0)
fromStart timeUnix timestamp
toEnd timeUnix timestamp
detailRequest detailed information of the symbolWhen true, indicates that it needs to be provided by the 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 spot exchange or futures exchange objects is set to custom data source (feeder), examples of requests sent by the backtesting system to the custom data source service:

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

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

  • Simulated-level Tick, here is a JSON data example:
    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] ] }
  • Live-level Tick, here is a JSON data example:
    Tick-level backtest data (includes order book depth information, depth format is an array of [price, quantity]. Can include multiple levels of depth, asks sorted in ascending order by price, bids sorted in descending order by price).
    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] ] }
FieldDescription
detailDetailed information of the requested instrument, including quote currency name, base currency name, precision, minimum order quantity, etc.
schemaSpecifies the column attributes in the data array, case-sensitive. Limited to time, open, high, low, close, vol, asks, bids, trades
dataData recorded according to the column structure set by schema

detail field

FieldDescription
eidExchange ID, note that spot and futures of the same exchange use different eids
symbolTrading instrument code
aliasThe corresponding symbol of the current trading instrument code on the exchange
baseCurrencyBase currency
quoteCurrencyQuote currency
marginCurrencyMargin currency
basePrecisionBase currency precision
quotePrecisionQuote currency precision
minQtyMinimum order quantity
maxQtyMaximum order quantity
minNotionalMinimum order amount
maxNotionalMaximum order amount
priceTickMinimum price tick size
volumeTickMinimum volume tick size
marginLevelFutures leverage multiplier
contractTypeFor perpetual contracts set to: swap, the backtest system will continue to send funding rate and price index requests

Special column attributes asks, bids, trades description:

FieldDescriptionRemarks
asks / bids[[price, quantity], ...]For example, data in the Live-level Tick data example: [[9531300, 10]]
trades[[time, direction(0:buy,1:sell), price, quantity], ...]For example, data in the Live-level Tick data example: [[1564315200000, 0, 9531300, 10]]

When backtesting perpetual contracts on futures exchanges, custom data sources also need to provide additional funding rate data and price index data. Only when the requested market data is returned and the detail field in the return structure contains the "contractType": "swap" key-value pair, will the backtest system continue to send funding rate requests.
After the backtest system receives the funding rate data, it will continue to send price index data requests.

Funding rate data structure is as follows:

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 ] // ... ] }
  • Adjacent period interval is 8 hours
  • For example, Binance funding rate updates every 8 hours, why is the funding rate data -16795?
    This is because, like K-line data, to avoid floating-point precision loss during network transmission, data is represented as integers; funding rate data can also be negative.

Example of funding rate data request sent by the backtest system:

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

Price index data structure is as follows:

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

Example of price index data request sent by the backtest system:

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

Specify the data source address, for example: http://120.24.2.20:9090/data. The custom data source service program is written in 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] ] } */ // /* 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}, }, } // */ /* Live trading-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:

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

FMZ Quant Trading Platform has open-sourced local backtesting engines for JavaScript and Python languages, supporting custom underlying K-line periods during backtesting.

Using Python as an example, here's a brief guide on how to use the local backtesting engine:

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 ----------------------------------- # Initialize the backtesting engine, backtest contains the engine configuration # which is consistent with the FMZ platform's online backtesting system configuration # Read the configuration string above via __doc__ and initialize the backtesting environment from fmz import * task = VCtx(__doc__) # initialize backtest engine from __doc__ # End ----------------------------------- # Part 2 ----------------------------------- # Below is an example of strategy code to be tested (complete strategy code can be copied from FMZ platform) # Note: When copying strategy code only, it does not include parameter design, interaction design, and other configurations def onTick(): ticker = _C(exchange.GetTicker) LogStatus(_D(), ticker.Last) def main(): exchange.SetCurrency("ETH_USDT") # exchange.SetContractType("swap") # If testing futures exchange objects, you need to set the contract, for example, set to perpetual contract here Log(exchange.GetAccount()) while True: onTick() Sleep(1000) # End ----------------------------------- # Part 3 ----------------------------------- # Execute backtesting and catch the termination signal, EOF exception will be triggered when backtesting ends # After catching the exception, you can output backtesting result data or display backtesting charts try: main() except: print("Strategy testing completed.") print(task.Join(False)) # print backtest result # task.Show() # or show backtest chart # End -----------------------------------

  • Shortcut for switching between strategy editor and backtest page
    Use Ctrl + , to switch between backtest page and strategy editor page. Hold Ctrl and press ,.
  • Shortcut for saving strategy
    Use Ctrl + s to save strategy.
  • Shortcut for starting backtest
    Use Ctrl + b to start backtest.

  • Backtest System Log Data Download
    Open the specific strategy and switch to the "Backtest Page" to run strategy backtesting. After the backtest is completed, there is a "Download Table" button in the upper right corner of the displayed "Status Information" bar. Click it to download the CSV format file of the status bar data at the end of the backtest.
  • Backtest System Status Bar Data Download
    Open the specific strategy and switch to the "Backtest Page" to run strategy backtesting. After the backtest is completed, there is a "Download Table" button in the upper right corner of the displayed "Log Information" bar. Click it to download the CSV format file of the backtest log data.

Backtesting system Sharpe ratio algorithm source code:

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 } }