Based on this framework, porting to the digital currency market is very simple. With just a few changes and extensions, you can extend into a very powerful trading strategy based on MACD logic.
Note that this strategy is just a code framework to give you a basic framework for expanding your strategy space. Studying this strategy logic framework of the FMZ platform can help you quickly develop the strategy you want. The purpose of the design framework is to save your time. In the FMZ platform, not only the underlying interface technology details, but also the strategy level. The basic framework we also help you solve.
In the future, we will have more strategy models for the basic framework, by doing it so, you can truly pay attention to the strategy itself, and only focus on the construction of the “onTick” function on our FMZ platform.
class Trader: # Declare a python class
def __init__(self, q, symbol): # The constructor of the Trader class, the parameter self (representing the object after the class is instantiated),
# q (the transaction processing object constructed by reference to the commodity futures trading class library template),
# symbol (commodity futures contract code)
self.q = q # Add the attribute q to the object constructed by the constructor and assign it with the parameter q.
self.symbol = symbol # As above, add the symbol attribute to the constructed object and assign it with the parameter symbol.
self.position = 0 # Add the attribute position assignment 0, which is used to record the number of positions.
self.isPending = False # Add the attribute isPending to assign False, which is used to mark the state of the object and whether it is suspended.
def onOpen(self, task, ret): # The class member function performs the callback function after the completion of the open position (that is, after the object q that
# simulates the multi-threaded transaction completes the current task, the callback to the onOpen function handles some post-opening work.)
if ret: # The transaction processing object q will call back onOpen after processing the transaction task, and pass in 2 parameters.
# The first one is received by the parameter task, the specific data is the executed transaction task data,
# and the second parameter is the transaction completion.
# If ret is valid data (the transaction is unsuccessful, ret is None), then the if block code is processed.
self.position = ret['position']['Amount'] * (1 if (ret['position']['Type'] == PD_LONG or ret['position']['Type'] == PD_LONG_YD) else -1)
# Assign the value of the attribute position of the object that calls the function, ret['position']['Amount'] is the number of
# positions after the transaction, according to ret['position']['Type'], the position type is equal to PD_LONG (holding long positions)
# Or PD_LONG_YD (short position) to choose ret['position']['Amount'] multiplied by 1 or -1, and finally assign the number
# of positions to parameter position, (the role is to distinguish between long positions or short positions by parameter position)
Log(task["desc"], "Position:", self.position, ret) # Print multiple items: the description of the data task dictionary of the task executed by q,
# the assigned position, the completion of the transaction processed by the q object (current position information)
self.isPending = False # Assigning value to isPending means that the current variety trading logic is in a non-suspended state and can accept tasks.
def onCover(self, task, ret): # The callback function to be executed after the closing task is completed, the parameters are the same as onOpen
self.isPending = False # Set the trading logic of isPending to False, which is the current variety to be non-suspended and accept the task.
self.position = 0 # The variable position of the record position is assigned a value of 0, that is, there is no position.
Log(task["desc"], ret) # Print the transaction description object q the task description (desc) of this processing, the result of the completion of the processing (ret)
def onTick(self): # The main trading logic, the core of the MACD strategy.
if self.isPending: # If the isPending attribute of the current logical object constructed by the Trader class is True, it means
# that the current transaction task is executing in the transaction processing object q queue.
return # The trading logic is in a suspended state and no processing is done.
ct = exchange.SetContractType(self.symbol) # According to the constructor passed in the symbol assigned to the object member property symbol passed to the API function
# ie: the SetContractType function of the exchange object exchange, used to set the contract type of the operation
if not ct: # The SetContractType function will return the details of the contract after the transaction contract code (symbol) succeeds.
# If it returns None, ie not ct is true, it will return immediately and wait for the next round.
r = exchange.GetRecords() # Declare the variable r (used to store the K line data), call the API function GetRecords to get the set K line data of the contract, and assign it to r.
if not r or len(r) < 35: # If r is None or r is less than 35 (because the MACD indicator is to be calculated,
# there must be a sufficient number of K-bars, less than 35 cannot be calculated)
return # Return immediately, the next round of processing.
macd = TA.MACD(r) # Call the API indicator function TA.MACD, pass in the argument r, calculate the MACD indicator data, and assign it to the variable macd.
# (The data that TA.MACD successfully returns is a two-dimensional array [dif, dea, quantum column])
# Do not understand dif, dea can google MACD indicators
diff = macd[-2] - macd[-2] # Calculate the difference between dif and dea (note that the calculation here uses the calculated dif and dea of the penultimate bar,
# because the K line of the last bar of the last number is constantly changing, and the macd indicator is always changing, only the
# the penultimate bar is accurate)
if abs(diff) > 0 and self.position == 0: # Open position, if the absolute value of the indicator (dif - dea) is greater than 0 at this moment
# and there is no position (ie: position is equal to 0)
self.isPending = True # Change state is set to suspend state, isPending = True
self.q.pushTask(exchange, self.symbol, ("buy" if diff > 0 else "sell"), 1, self.onOpen) # Call the member function pushTask of the transaction processing object q to issue the
# open trading task, parameter: exchange trading platform object (passing)
# Self.symbol contract code (passing when constructing), set "buy" or "sell" according to whether diff is greater than 0 or less than 0. 1
# This parameter refers to the order quantity 1 unit, self.onOpen reference to the incoming callback function
if abs(diff) > 0 and ((diff > 0 and self.position < 0) or (diff < 0 and self.position > 0)): # Closing the position, if the absolute value of the indicator (dif - dea) is greater
# than 0 at this moment and the condition after "and" is established, the code in the if block is executed.
# Diff is greater than 0 and holds a short position or diff is less than 0 and holds long positions, all of which are closed position condition.
self.isPending = True # Set to suspend state.
self.q.pushTask(exchange, self.symbol, ("closebuy" if self.position > 0 else "closesell"), 1, self.onCover) # Send the closing trading task, the parameters are the same as the above to
# send the opening position task.
def main(): # Entry function
q = ext.NewTaskQueue() # The export function (ie interface) of the Python version of the commodity futures trading class library template is called,
# and ext.NewTaskQueue returns a constructed trading processing object. Reference and assign it to variable q
Log(_C(exchange.GetAccount)) # Start calling the _C fault-tolerant function, passing in the API for fault-tolerant processing: GetAccount function,
# return account information and output to the log by the Log function.
tasks =  # Declare an empty array tasks.
for symbol in ["MA701", "rb1701"]: # Traversing the elements in the array ["MA701", "rb1701"], each time the element symbol and the transaction processing object
# q are passed as parameters to the constructor of the Trader class to construct the trading logic object.
tasks.append(Trader(q, symbol)) # The constructed trading logic object is pushed into the tasks array. Used to loop through the execution process.
while True: # Set a while infinite loop
if exchange.IO("status"): # Call the API function IO every time, pass the parameter "status" to detect the connection status with the futures company's
# front-end server (CTP protocol), return True, connect the transaction server and the market server.
for t in tasks: # Traversing the tasks array, calling the member function onTick of the constructed property of the Trader class,
# constantly checking the market, opening and closing the position.
t.onTick() # See the onTick function in the Trader class
q.poll() # The member function poll of the transaction processing object q is called to process the transaction task in the queue within the q object.
Sleep(1000) # The program pauses for a period of time each time the while loop (1000): pause for 1 second (1000 milliseconds) to avoid accessing the API too frequently.