Teach you to transform a Python single-species strategy into a multi-species strategy
Tags: strategy, fmzquant, python, trading
Published: 2024-05-13 01:19:20

### I. Teach you to transform a Python single-currency strategy into a multi-currency strategy
In the last article, a very simple Python strategy was implemented: ["Strategy for buying the winners of Python version"](https://www.fmz.com/digest-topic/9839), this strategy can operate an account to conduct program trading on a certain trading pair. The principle is very simple, that is, chasing after increasing and killing after decreasing. Sometimes we want to use the same trading logic to operate different trading pairs. You can create multiple robots and set different trading pairs to conduct transactions in various currencies. If the strategy is not very complex, in view of the strong flexibility of the FMZ Quant Trading Platform, it is easy to transform a strategy into a multi-species strategy, so that you can run multiple trading pairs by creating only one robot.

Strategy source code after transformation:

```
'''backtest
start: 2019-02-20 00:00:00
end: 2020-01-10 00:00:00
period: 1m
exchanges: [{"eid":"OKEX","currency":"BTC_USDT"},{"eid":"OKEX","currency":"ETH_USDT","stocks":30},{"eid":"OKEX","currency":"LTC_USDT","stocks":100}]
'''

import time
import json

params = {
    "arrBasePrice": [-1, -1, -1],     # -1
    "arrRatio": [0.05, 0.05, 0.05],         # 0.05
    "arrAcc": [],           # _C(exchange.GetAccount)
    "arrLastCancelAll": [0, 0, 0], # 0
    "arrMinStocks": [0.01, 0.01, 0.01],     # 0.01
    "arrPricePrecision": [2, 2, 2], # 2
    "arrAmountPrecision": [3, 2, 2], # 2
    "arrTick":[]
}

def CancelAll(e):
    while True : 
        orders = _C(e.GetOrders)
        for i in range(len(orders)) :
            e.CancelOrder(orders[i]["Id"], orders[i])
        if len(orders) == 0 :
            break
        Sleep(1000)

def process(e, index):
    global params
    ticker = _C(e.GetTicker)
    params["arrTick"][index] = ticker
    if params["arrBasePrice"][index] == -1 :
        params["arrBasePrice"][index] = ticker.Last
    if ticker.Last - params["arrBasePrice"][index] > 0 and (ticker.Last - params["arrBasePrice"][index]) / params["arrBasePrice"][index] > params["arrRatio"][index]:
        params["arrAcc"][index] = _C(e.GetAccount)
        if params["arrAcc"][index].Balance * params["arrRatio"][index] / ticker.Last > params["arrMinStocks"][index]:
            e.Buy(ticker.Last, params["arrAcc"][index].Balance * params["arrRatio"][index] / ticker.Last)
            params["arrBasePrice"][index] = ticker.Last
    if ticker.Last - params["arrBasePrice"][index] < 0 and (params["arrBasePrice"][index] - ticker.Last) / params["arrBasePrice"][index] > params["arrRatio"][index]: 
        params["arrAcc"][index] = _C(e.GetAccount)
        if params["arrAcc"][index].Stocks * params["arrRatio"][index] > params["arrMinStocks"][index]:
            e.Sell(ticker.Last, params["arrAcc"][index].Stocks * params["arrRatio"][index])
            params["arrBasePrice"][index] = ticker.Last
    ts = time.time()
    if ts - params["arrLastCancelAll"][index] > 60 * 5 :
        CancelAll(e)
        params["arrLastCancelAll"][index] = ts 

def main():
    global params
    
    for i in range(len(exchanges)) :    
        params["arrAcc"].append(_C(exchanges[i].GetAccount))
        params["arrTick"].append(_C(exchanges[i].GetTicker))
        exchanges[i].SetPrecision(params["arrPricePrecision"][i], params["arrAmountPrecision"][i])

    for key in params :
        if len(params[key]) < len(exchanges):
            raise "params error!"

    while True:
        tblAcc = {
            "type" : "table",
            "title": "account",
            "cols": ["Account information"], 
            "rows": []
        }        

        tblTick = {
            "type" : "table",
            "title": "ticker",
            "cols": ["Market information"], 
            "rows": []
        }
        for i in range(len(exchanges)): 
            process(exchanges[i], i)

        for i in range(len(exchanges)):
            tblAcc["rows"].append([json.dumps(params["arrAcc"][i])])
            tblTick["rows"].append([json.dumps(params["arrTick"][i])])

        LogStatus(_D(), "\n`" + json.dumps([tblAcc, tblTick]) + "`")
        Sleep(500)
```

### II. Find a difference
By comparing the code, do you find that it is very different from the code in the previous article?
In fact, the trading logic is exactly the same, without any change. We only modify the strategy to multiple-species one, we cannot use the previous form of "single variable as strategy parameter". A more reasonable solution is to make the parameter into an array, and the index of each position in the array corresponds to the added trading pair.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rlmz4xanjlmlbpwkvmep.png)

Then encapsulate the code of trading logic into a function process. On the main strategy loop, call this function iteratively according to the added trading pairs, and let each trading pair execute the trading logic code once.
- Iterative (traversal) call:

```
for i in range(len(exchanges)): 
    process(exchanges[i], i)
```
- Strategy parameters:
```
params = {
    "arrBasePrice": [-1, -1, -1],           # -1
    "arrRatio": [0.05, 0.05, 0.05],         # 0.05
    "arrAcc": [],                           # _C(exchange.GetAccount)
    "arrLastCancelAll": [0, 0, 0],          # 0
    "arrMinStocks": [0.01, 0.01, 0.01],     # 0.01
    "arrPricePrecision": [2, 2, 2],         # 2
    "arrAmountPrecision": [3, 2, 2],        # 2
    "arrTick":[]
}
```

This design allows each trading pair to have its own parameters, because each trading pair may have a large price difference, and the parameters may also be different, sometimes requiring differential settings.

- CancelAll function
You can compare the change of this function. This function only modifies a little code, and then think about the intention of such modification.

- Status bar chart data
A chart is added to display the market data and account asset data in the status bar, so that the corresponding assets and market of each exchange object can be displayed in real time.
Is it easy to change a Python strategy into a multi-species strategy after mastering the above design ideas?

### III. Backtest

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qf2rbc0xt4bu6i8ki5ou.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5tt6ey7l6034reqixarw.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5fenwntk1u6t9o3l08ou.png)

The strategy is for learning and backtesting purposes only, and you can optimizenand upgrade it if you are interested.
[Strategy address](https://www.fmz.com/strategy/182268)

From: https://blog.mathquant.com/2022/12/20/teach-you-to-transform-a-python-single-species-strategy-into-a-multi-species-strategy.html