Backtesting System
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.
Backtesting System Modes
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. CallGetDepthandGetTradesfunctions to obtain playback market data. At a certain market data moment on the timeline, callingGetTicker,GetTrades,GetDepth,GetRecordswill 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.
Impact of Backtest Data Granularity on Backtesting
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)
}
}
Backtesting System Supports Multiple Programming Languages
The backtesting system supports: JavaScript, TypeScript, Python, C++, PINE, My Language, Blockly visual programming, and Workflow workflow-designed strategies for backtesting.
-
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.
-
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.
-
JavaScript strategies support debugging in Chrome browser DevTools during backtesting. Reference documentation.
-
Workflow strategies support backtesting, with visual display of node execution status and data flow.
Exchanges Supported by Backtesting System
-
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} }
Backtest System Parameter Optimization
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 forJavaScript,PINE, andMyLanguagestrategies, 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.
Save Backtest Settings
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?
-
- 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.
-
- 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?
-
- 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.
-
- If there is no backtest configuration information recorded in comment form
backtestin 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.
- If there is no backtest configuration information recorded in comment form
-
- 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
backtestin the strategy editing area.
- 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
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/PINElanguage 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"}] */
Custom Data Source
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:
| Parameter | Meaning | Description |
|---|---|---|
| symbol | Symbol name | Spot 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 |
| eid | Exchange | For example: OKX, Futures_OKX |
| round | Data precision | When 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 |
| period | K-line data period (milliseconds) | For example: 60000 represents a 1-minute period |
| depth | Order book depth levels | 1-20 |
| trades | Whether tick data is required | True (1) / False (0) |
| from | Start time | Unix timestamp |
| to | End time | Unix timestamp |
| detail | Request detailed information of the symbol | When 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
Data Format
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,askssorted in ascending order by price,bidssorted 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] ] }
| Field | Description |
|---|---|
| detail | Detailed information of the requested instrument, including quote currency name, base currency name, precision, minimum order quantity, etc. |
| schema | Specifies the column attributes in the data array, case-sensitive. Limited to time, open, high, low, close, vol, asks, bids, trades |
| data | Data recorded according to the column structure set by schema |
detail field
| Field | Description |
|---|---|
| eid | Exchange ID, note that spot and futures of the same exchange use different eids |
| symbol | Trading instrument code |
| alias | The corresponding symbol of the current trading instrument code on the exchange |
| baseCurrency | Base currency |
| quoteCurrency | Quote currency |
| marginCurrency | Margin currency |
| basePrecision | Base currency precision |
| quotePrecision | Quote currency precision |
| minQty | Minimum order quantity |
| maxQty | Maximum order quantity |
| minNotional | Minimum order amount |
| maxNotional | Maximum order amount |
| priceTick | Minimum price tick size |
| volumeTick | Minimum volume tick size |
| marginLevel | Futures leverage multiplier |
| 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 description:
| Field | Description | Remarks |
|---|---|---|
| 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
Custom Data Source Example
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)
}
Local Backtesting Engine
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 -----------------------------------
Backtest Page Shortcuts
- Shortcut for switching between strategy editor and backtest page
UseCtrl + ,to switch between backtest page and strategy editor page. HoldCtrland press,. - Shortcut for saving strategy
UseCtrl + sto save strategy. - Shortcut for starting backtest
UseCtrl + bto start backtest.
Backtest Data Download
- 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.
Backtest System Sharpe Ratio Algorithm
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
}
}