Many developers who write strategies in Python want to put the strategy code files locally, worrying about the safety of the strategy. As a solution proposed in the FMZ API document:
Strategy security
The strategy is developed on the FMZ platform, and the strategy is only visible to the FMZ account holders. And on the FMZ platform, the strategy code can be completely localized, for example, the strategy is encapsulated into a Python package and loaded in the strategy code, so that the strategy localization is realized.
For more details, please go to: https://www.fmz.com/api
In fact, this kind of worry is not necessary, but since there are such needs, we shall provide a complete implementation example.
Encapsulate a strategy
Let's find a simple Python strategy for demonstration, using the classic Dual Thrust
strategy, strategy address: https://www.fmz.com/strategy/21856
We strive not to change any part of the strategy code, encapsulate the strategy into a file that can be called by the strategy code on the FMZ platform, and the execution result is exactly the same as running the strategy directly. The biggest problem with encapsulation is that the global objects, global functions, and constant values called by the strategy code on the FMZ platform cannot be accessed in the files we encapsulate, so we must find a way to pass these objects, functions, variables, and constants to the encapsulated file. let's do it step by step.
- Copy the Python version of the Dual Thrust OKCoin futures strategy code and paste it into the local Python file. The local Python file is named
testA
.
Paste into the file testA
opened by the local editor.
Add some code, and keep the strategy code part copied and pasted intact
# Function, object exchanges = None exchange = None Log = None Sleep = None TA = None Chart = None LogProfitReset = None LogStatus = None _N = None _C = None LogProfit = None # Strategy parameters ContractTypeIdx = None MarginLevelIdx = None NPeriod = None Ks = None Kx = None AmountOP = None Interval = None LoopInterval = None PeriodShow = None # constant ORDER_STATE_PENDING = 0 ORDER_STATE_CLOSED = 1 ORDER_STATE_CANCELED = 2 ORDER_STATE_UNKNOWN = 3 ORDER_TYPE_BUY = 0 ORDER_TYPE_SELL = 1 PD_LONG = 0 PD_SHORT = 1 def SetExchanges(es): global exchanges, exchange exchanges = es exchange = es[0] def SetFunc(pLog, pSleep, pTA, pChart, pLogStatus, pLogProfitReset, p_N, p_C, pLogProfit): global Log, Sleep, TA, Chart, LogStatus, LogProfitReset, _N, _C, LogProfit Log = pLog Sleep = pSleep TA = pTA Chart = pChart LogStatus = pLogStatus LogProfitReset = pLogProfitReset _N = p_N _C = p_C LogProfit = pLogProfit def SetParams(pContractTypeIdx, pMarginLevelIdx, pNPeriod, pKs, pKx, pAmountOP, pInterval, pLoopInterval, pPeriodShow): global ContractTypeIdx, MarginLevelIdx, NPeriod, Ks, Kx, AmountOP, Interval, LoopInterval, PeriodShow ContractTypeIdx = pContractTypeIdx MarginLevelIdx = pMarginLevelIdx NPeriod = pNPeriod Ks = pKs Kx = pKx AmountOP = pAmountOP Interval = pInterval LoopInterval = pLoopInterval PeriodShow = pPeriodShow
The main function of the above code is to declare the global functions and variables used in the current file. Then reserve the interfaces SetExchanges
, SetParams
, SetFunc
to import these functions. Strategies on the FMZ platform call these functions and pass over some used functions and objects.
Startup strategy on FMZ platform
The startup strategy is very simple, as follows:
There are only a few lines of code written on the FMZ platform. It should be noted that the parameters of this startup strategy are exactly the same as our packaged strategy Python version of the Dual Thrust OKCoin futures strategy code. In fact, you can directly copy Python version of the Dual Thrust OKCoin futures strategy code Strategy, then just clear the strategy code, paste it.
import sys # Here I wrote the path where I put the testA file myself. I replaced it with xxx. To put it simply, I set the path of my testA file. sys.path.append("/Users/xxx/Desktop/pythonPlayground/") import testA def main(): # Passing Exchange Object testA.SetExchanges(exchanges) # Pass global function SetFunc(pLog, pSleep, pTA, pChart, pLogStatus, pLogProfitReset, p_N, p_C, pLogProfit) testA.SetFunc(Log, Sleep, TA, Chart, LogStatus, LogProfitReset, _N, _C, LogProfit) # Passing strategy parameters SetParams(pContractTypeIdx, pMarginLevelIdx, pNPeriod, pKs, pKx, pAmountOP, pInterval, pLoopInterval, pPeriodShow) testA.SetParams(ContractTypeIdx, MarginLevelIdx, NPeriod, Ks, Kx, AmountOP, Interval, LoopInterval, PeriodShow) # Execute the main strategy function in the encapsulated testA file testA.main()
In this way, we encapsulate the main body of the strategy logic in the testA
file and place it locally on the device where the docker is located. On the FMZ platform, we only need to save a startup strategy. The robot that creates this startup strategy can directly load our local file and run it locally.
Backtesting comparison
- Load
testA
file locally for backtest
- Original strategy, backtesting on public server
Another simpler way
Load the file directly for execution.
This time we prepare a testB
file with the code for the Python version of the Dual Thrust OKCoin futures strategy code strategy.
import time class Error_noSupport(BaseException): def __init__(self): Log("Only OKCoin futures are supported!#FF0000") class Error_AtBeginHasPosition(BaseException): def __init__(self): Log("There is a futures position at startup!#FF0000") ChartCfg = { '__isStock': True, 'title': { 'text': 'Dual Thrust Top and bottom rail map' }, 'yAxis': { ...
If the strategy is too long, it is omitted and the strategy code does not need to be changed at all.
Then prepare Python version of the Dual Thrust OKCoin futures strategy code (start strategy, directly execute testB
file), which is our strategy on the FMZ platform, create a robot, directly load the testB
file, and execute it directly. It should be noted that the startup strategy must also have exactly the same strategy parameter settings (strategy interface parameters) as the original version of Python version of the Dual Thrust OKCoin futures strategy code.
if __name__ == '__main__': Log("run...") try: # The file path is processed, you can write the actual path of your testB file f = open("/Users/xxx/Desktop/pythonPlayground/testB.py", "r") code = f.read() exec(code) except Exception as e: Log(e)
Perform a backtest:
The backtest result is consistent with the above test.
Obviously the second method above is simpler, it is recommended to use.