[PYTHON] I tried to summarize the methods that are often used when implementing basic algo in Quantx Factory

Self-introduction

My name is Maskawa (https://www.facebook.com/ryozodesert) and I am an intern at SmartTrade Co., Ltd.. .. I'm studying machine learning.

What is QuantX Factory?

Smart Trade Ltd.

Financial democratization

Based on this philosophy, we have developed a platform "QuantX Factory" that allows anyone to easily develop trading algorithms.

Strengths

--No need to build an environment that tends to frustrate beginners --Various data such as closing price, high price, and trading volume of each brand are prepared, No need to prepare data set </ font> --The developed trading algorithm can be sold at the QuantX Store if it passes the examination. --For advanced users, there is also a development environment such as QuantX Lab (Source: Official Document)

Main subject

In my first month as an intern, I will explain how to use the pandas and talib methods that are frequently used in creating algorithms to implement basic technical indicators such as MACD, moving averages, and RSI.

(Note: You don't have to fully understand the contents of this code right now) </ font>

#Library import
#Required library
#Import backtest engine "maron"
import maron
import maron.signalfunc as sf
import maron.execfunc as ef
#Additional library
#If you need additional libraries, add them according to the example below.
#Import the data analysis tool "pandas" and use it under the name "pd"
import pandas as pd
#Import the financial function set "talib" and use it with the name "ta"
import talib as ta
import numpy as np
#Import "" and use it as ""
# import  as 

#Order method(Please uncomment only one of the following three according to the desired ordering method)
# ot = maron.OrderType.MARKET_CLOSE  #Order at the timing of the closing price the day after the signal is issued
ot = maron.OrderType.MARKET_OPEN   #Order at the timing of the opening price the day after the signal is issued
# ot = maron.OrderType.LIMIT         #Limit order

def initialize(ctx):  #Initialization part
  ctx.logger.debug("initialize() called")  #Log output
  ctx.target = 0.1
  ctx.loss_cut = -0.02
  ctx.plofit = 0.05
  
  ctx.configure(
      channels={
        "jp.stock": {
          "symbols": [
            "jp.stock.2914", #JT(Japan Tobacco)
            "jp.stock.8766", #Tokio Marine Holdings
            "jp.stock.8031", #Mitsui
            "jp.stock.8316", #Sumitomo Mitsui Financial Group
            "jp.stock.8411", #Mizuho Financial Group
            "jp.stock.9437", #NTT DoCoMo
            "jp.stock.4502", #Takeda Pharmaceutical Company
            "jp.stock.8058", #Mitsubishi Corporation
            "jp.stock.9433", #KDDI
            "jp.stock.9432", #Nippon Telegraph and Telephone
            "jp.stock.7267", #Honda (Honda Motor Co., Ltd.)
            "jp.stock.8306", #Mitsubishi UFJ Financial Group
            "jp.stock.4503", #Astellas Pharma
            "jp.stock.4063", #Shin-Etsu Chemical
            "jp.stock.7974", #Nintendo
            "jp.stock.6981", #Murata Manufacturing Co., Ltd.
            "jp.stock.3382", #Seven & i Holdings
            "jp.stock.9020", #East Japan Railway Company
            "jp.stock.8802", #Mitsubishi Estate
            "jp.stock.9022", #Central Japan Railway Company
            "jp.stock.9984", #Softbank Group
            "jp.stock.6861", #Keyence
            "jp.stock.6501", #Hitachi
            "jp.stock.6752", #Panasonic
            "jp.stock.6758", #Sony
            "jp.stock.6954", #FANUC
            "jp.stock.7203", #Toyota Motor
            "jp.stock.7751", #Canon
            "jp.stock.4452", #Kao
            "jp.stock.6098", #Recruit Holdings
          ],
          #⑥
          "columns": ["close_price_adj",    #closing price(After stock split adjustment) 
          ]}})
  
  def _my_signal(data):  #Trading signal generation part
    #If you want to check the contents of data, please uncomment the following
    # ctx.logger.debug(data)

    syms = data.minor_axis   #Creating a stock list
    dates = data.major_axis  #Creating a date list
    
    '''↓ Write the code to get the data required for logic calculation from the 3D structure data ↓'''
    cp = data["close_price_adj"].fillna("ffill")
    '''↑ Write the code to get the data required for logic calculation from the 3D structure data ↑'''
    
    '''↓ Write the code to calculate the logic required to define the trading conditions ↓'''
    
    
    movave5 = cp.rolling(window = 5, center = False).mean()
    movave25 = cp.rolling(window = 25, center = False).mean()
    
    '''↑ Write the code to calculate the logic required to define the trading conditions ↑'''
    
    #Define trading signals(Returns as a bool value)
    buy_sig = (movave5 > movave25) & (movave5.shift(1) < movave25.shift(1))
    sell_sig =  (movave5 < movave25) & (movave5.shift(1) > movave25.shift(1))

    # market_All 0 called sig.Create a data frame of "horizontal: brand name, vertical: date" that stores 0
    market_sig = pd.DataFrame(data=0.0, columns=syms, index=dates)
    
    # buy_1 when sig is True.0、sell_When sig is True-1.0, 0 when both are True.Set to 0
    market_sig[buy_sig == True] = 1.0
    market_sig[sell_sig == True] = -1.0
    market_sig[(buy_sig == True) & (sell_sig == True)] = 0.0

    # market_If you want to check the contents of sig, please uncomment the following
    # ctx.logger.debug(market_sig)

    return {
      "buy:sig":buy_sig,
      "sell:sig": sell_sig,
      "market:sig": market_sig,
      #Please add the data you want to display in the backtest result chart below
      
    }
      
  ctx.regist_signal("my_signal", _my_signal)  #Signal registration

def handle_signals(ctx, date, current):  #Daily processing part
  '''
  current: pd.DataFrame
  '''
  #initialize_my_Market the signal generated by signal_Store in sig
  market_sig = current["market:sig"]
  done_syms = set([])  #Set type that stores stocks that have been profit-taking and loss-cutting
  none_syms = set([])  # portfolio.Set type that stores stocks that do not exist in positions
  # portfolio.positions(Brands you own)Target stocks(sym)Check if there is
  for (sym, val) in market_sig.items():
    if sym not in ctx.portfolio.positions:
      none_syms.add(sym)
  # portfolio.positions(Brands you own)Each brand of(sym)Check if the number of shares held by
  for (sym, val) in ctx.portfolio.positions.items():
    if val["amount"] == 0:
      none_syms.add(sym)
  #Loss cut, profit taking(Profitability)settings of
  #Iterative process to check the stocks you own one by one
  for (sym, val) in ctx.portfolio.positions.items():
    #Acquisition of profit / loss ratio
    returns = val["returns"]
    if returns < -0.03:  #Profit and loss ratio-3%Less than(Absolute value 3%Greater loss)in the case of
      #Sell order for loss cut
      sec = ctx.getSecurity(sym)
      sec.order(-val["amount"], comment="Loss cut(%f)" % returns)
      #Added the issue assigned to sym to the set type that stores the issue for which profit settlement and loss cut have been performed.
      done_syms.add(sym)
    elif returns > 0.05:  #Profit and loss ratio+5%If greater than
      #Profit taking(Profitability)Sell order for
      sec = ctx.getSecurity(sym)
      sec.order(-val["amount"], comment="Profit-taking sale(%f)" % returns)
      #Added the issue assigned to sym to the set type that stores the issue for which profit settlement and loss cut have been performed.
      done_syms.add(sym)
  buy = market_sig[market_sig > 0.0]  #Buy signal
  for (sym, val) in buy.items():  #Process stocks with buy signals one by one
    # done_syms or none_If syms has sym
    if sym in done_syms:
      continue  #Skip processing
    #Buy order
    sec = ctx.getSecurity(sym)
    sec.order(sec.unit() * 1, orderType=ot, comment="SIGNAL BUY")
    #If you want to output the buy order log below, please uncomment the following(Long-term caution)
    #ctx.logger.debug("BUY: %s,  %f" % (sec.code(), val))
  sell = market_sig[market_sig < 0.0]  #Sell signal
  for (sym, val) in sell.items():  #Process stocks with sell signals one by one
    # done_syms or none_If syms has sym
    if (sym in done_syms) | (sym in none_syms):
      continue  #Skip processing
    #Sell order
    sec = ctx.getSecurity(sym)
    sec.order(sec.unit() * -1,orderType=ot, comment="SIGNAL SELL")

Figure 1: Algorithm using golden cross and dead cross of moving average (see: https://factory.quantx.io/developer/149d79a8cf744f059af0e96918913a9f/coding / coding))

pandas series

** rolling method **

pd.DataFrame.rolling(window)

--A method used to apply window functions to pandas basic data structures such as DataFrame and Series, which returns a rolling object. --The main argument used is window, which sets the number of windows. --Window function: Function that becomes 0 except for a certain finite interval </ font>. -* When a window function is multiplied by a certain function or signal (data), the outside of the interval becomes 0, and only the inside of the finite interval remains, which facilitates numerical analysis. * (Reference: Weblio "What is a window function?") --Usage: Technical indicators such as Moving Average, Volume Moving Average Used when implementing.

Example: Implement a 25-day moving average

In line 82 of FIG. movave25 = cp.rolling(window = 25).mean() It says, but this is what defines the 25-day moving average.

  1. Apply the window function with 25 windows with cp.rolling (window = 25) to cp, which is a DataFrame that stores the closing price of each date of each brand, and a rolling object is returned.
  2. Apply the mean method to the returned rolling object and it will return a new DataFrame type object that contains the average value for 25 days.

You now have move25, a DataFrame that stores 25 days' worth of moving averages.

shift method

pandas.DataFrame.shift(periods)

--Returns a DataFrame object, a method that moves (shifts) the data stored in a data frame by periods </ font> --Usage: Detects changes in the magnitude relationship between the two moving averages, the short-term line and the long-term line, from the previous day.

Example: Judgment of golden cross and dead cross In line 87 of FIG. buy_sig = (movave5 > movave25) & (movave5.shift(1) < movave25.shift(1)) However, this code indicates that "the 5-day moving average of the day is larger than the 25-day moving average, and the 5-day moving average of the previous day is smaller than the 25-day moving average", that is, a golden cross. I am. Line 88 is the opposite.

The shift method is used in this way.

talib system

What is talib in the first place?

A library that allows you to easily implement technical indicators. Please refer to the following URL for the indicators that can be implemented. https://mrjbq7.github.io/ta-lib/funcs.html

Example using talib

# Sample Algorithm
#Library import
#Required library
import maron
import maron.signalfunc as sf
import maron.execfunc as ef
#Additional library
#Please see the notes on the right screen for the libraries that can be used ①



import pandas as pd
import talib as ta
import numpy as np

#Order method(Please uncomment only one of the following two according to the desired ordering method)
#Please see the note on the right screen for the ordering method ②
#ot = maron.OrderType.MARKET_CLOSE #Order at the timing of the closing price the day after the signal is issued
ot = maron.OrderType.MARKET_OPEN   #Order at the timing of the opening price the day after the signal is issued
#ot = maron.OrderType.LIMIT        #Limit order

#Acquisition of stocks and columns
#Please see the note on the right screen for the designation of the brand ③
#Please see the note on the right screen for getting columns ④
def initialize(ctx):
  #Setting
  ctx.logger.debug("initialize() called")
  ctx.configure(
    channels={               #Channel used
      "jp.stock": {
        "symbols": [
            "jp.stock.2914", #JT(Japan Tobacco)
            "jp.stock.8766", #Tokio Marine Holdings
            "jp.stock.8031", #Mitsui
            "jp.stock.8316", #Sumitomo Mitsui Financial Group
            "jp.stock.8411", #Mizuho Financial Group
            "jp.stock.9437", #NTT DoCoMo
            "jp.stock.4502", #Takeda Pharmaceutical Company
            "jp.stock.8058", #Mitsubishi Corporation
            "jp.stock.9433", #KDDI
            "jp.stock.9432", #Nippon Telegraph and Telephone
            "jp.stock.7267", #Honda (Honda Motor Co., Ltd.)
            "jp.stock.8306", #Mitsubishi UFJ Financial Group
            "jp.stock.4503", #Astellas Pharma
            "jp.stock.4063", #Shin-Etsu Chemical
            "jp.stock.7974", #Nintendo
            "jp.stock.6981", #Murata Manufacturing Co., Ltd.
            "jp.stock.3382", #Seven & i Holdings
            "jp.stock.9020", #East Japan Railway Company
            "jp.stock.8802", #Mitsubishi Estate
            "jp.stock.9022", #Central Japan Railway Company
            "jp.stock.9984", #Softbank Group
            "jp.stock.6861", #Keyence
            "jp.stock.6501", #Hitachi
            "jp.stock.6752", #Panasonic
            "jp.stock.6758", #Sony
            "jp.stock.6954", #FANUC
            "jp.stock.7203", #Toyota Motor
            "jp.stock.7751", #Canon
            "jp.stock.4452", #Kao
            "jp.stock.6098", #Recruit Holdings
        ],
        "columns": [
          "close_price",     #closing price
          "close_price_adj", #closing price(After stock split adjustment)
          #"volume_adj",     #Volume
          #"txn_volume",     #Trading price
        ]
      }
    }
  )
  
  #Signal definition
  def _my_signal(data):
    #Get closing price data
    cp=data["close_price_adj"].fillna(method="ffill")
    
    syms = data.minor_axis   #Creating a stock list
    dates = data.major_axis  #Creating a date list
   
    #Where to store the data
    macd = pd.DataFrame(data=0.0, columns=syms, index=dates)
    macdsignal = pd.DataFrame(data=0.0, columns=syms, index=dates)
    macdhist = pd.DataFrame(data=0.0, columns=syms, index=dates)
    rsi = pd.DataFrame(data = 0.0, columns = syms, index = dates)

    #TA-Calculation of MACD by Lib
    for (sym,val) in cp.items():
      macd[sym],macdsignal[sym],macdhist[sym] = ta.MACD(cp[sym])
      rsi[sym] = ta.RSI(cp[sym].values.astype(np.double), timeperiod = 10)

    
    macd_golden = (macd > macdsignal) & (macd.shift(1) < macdsignal.shift(1))
    macd_dead = (macd < macdsignal) & (macd.shift(1) > macdsignal.shift(1))
    
    #Trading signal generation part
    
    buy_sig = macd_golden | (rsi < 30)
    sell_sig = macd_dead | (rsi > 70)

    #market_Create a data frame called sig that contains all 0s
    market_sig = pd.DataFrame(data=0.0, columns=syms, index=dates)
    
    #buy_1 when sig is True.0、sell_When sig is True-1.Set to 0
    market_sig[buy_sig == True] = 1.0
    market_sig[sell_sig == True] = -1.0
    market_sig[(buy_sig == True) & (sell_sig == True)] = 0.0
    # ctx.logger.debug(market_sig)

    return {
      "MACD:g2": macd, 
      "MACDSignal:g2": macdsignal, 
      "MACDHist": macdhist, 
      "market:sig": market_sig,
    }

  #Signal registration
  ctx.regist_signal("my_signal", _my_signal)

def handle_signals(ctx, date, current):  #Daily processing part
  '''
  current: pd.DataFrame
  '''
  #initialize_my_Market the signal generated by signal_Store in sig
  market_sig = current["market:sig"]
  done_syms = set([])  #Set type that stores stocks that have been profit-taking and loss-cutting
  none_syms = set([])  # portfolio.Set type that stores stocks that do not exist in positions
  # portfolio.positions(Brands you own)Target stocks(sym)Check if there is
  for (sym, val) in market_sig.items():
    if sym not in ctx.portfolio.positions:
      none_syms.add(sym)
  # portfolio.positions(Brands you own)Each brand of(sym)Check if the number of shares held by
  for (sym, val) in ctx.portfolio.positions.items():
    if val["amount"] == 0:
      none_syms.add(sym)
  #Loss cut, profit taking(Profitability)settings of
  #Iterative process to check the stocks you own one by one
  for (sym, val) in ctx.portfolio.positions.items():
    #Acquisition of profit / loss ratio
    returns = val["returns"]
    if returns < -0.03:  #Profit and loss ratio-3%Less than(Absolute value 3%Greater loss)in the case of
      #Sell order for loss cut
      sec = ctx.getSecurity(sym)
      sec.order(-val["amount"], comment="Loss cut(%f)" % returns)
      #Added the issue assigned to sym to the set type that stores the issue for which profit settlement and loss cut have been performed.
      done_syms.add(sym)
    elif returns > 0.05:  #Profit and loss ratio+5%If greater than
      #Profit taking(Profitability)Sell order for
      sec = ctx.getSecurity(sym)
      sec.order(-val["amount"], comment="Profit-taking sale(%f)" % returns)
      #Added the issue assigned to sym to the set type that stores the issue for which profit settlement and loss cut have been performed.
      done_syms.add(sym)
  buy = market_sig[market_sig > 0.0]  #Buy signal
  for (sym, val) in buy.items():  #Process stocks with buy signals one by one
    # done_syms or none_If syms has sym
    if sym in done_syms:
      continue  #Skip processing
    #Buy order
    sec = ctx.getSecurity(sym)
    sec.order(sec.unit() * 1, orderType=ot, comment="SIGNAL BUY")
    #If you want to output the buy order log below, please uncomment the following(Long-term caution)
    #ctx.logger.debug("BUY: %s,  %f" % (sec.code(), val))
  sell = market_sig[market_sig < 0.0]  #Sell signal
  for (sym, val) in sell.items():  #Process stocks with sell signals one by one
    # done_syms or none_If syms has sym
    if (sym in done_syms) | (sym in none_syms):
      continue  #Skip processing
    #Sell order
    sec = ctx.getSecurity(sym)
    sec.order(sec.unit() * -1,orderType=ot, comment="SIGNAL SELL")

Figure 2: Algorithm using RSI and MACD golden cross and dead cross (see: https://factory.quantx.io/developer/6ba1eb1b748d46a18ce128fea3156282/coding / coding))

RSI talib.RSI(close, timeperiod = 14)

――RSI is a technical index that distinguishes between “overbought” and “oversold”. --It takes np.double type np.arrays object close indicating the closing price and timeperiod indicating the period as arguments.

Example: At line 82 of the code in Figure 2.

rsi = pd.DataFrame(data = 0.0, columns = syms, index = dates)

Define a DataFrame to store the RSI of each issue

For statement on line 85

for (sym,val) in cp.items():
      rsi[sym] = ta.RSI(cp[sym].values.astype(np.double), timeperiod = 10)

The RSI of each brand is stored in.

--Since cp [sym] is a DataFrame type object, convert it to an array type object with cp [sym] .values. --And cp [sym] .values.astype (np.double) converts the contents of the array to np.double type. --This time, we will take RSI for 10 days, so set timeperiod = 10. ――Judge buying and selling by referring to whether it is "oversold" or "overbought"

MACD ta.MACD(close)

――It is a technical index that applies the moving average line, and measures the timing of buying and selling by combining the MACD line and the Japanese line of the MACD signal line. --Take a Series object close that stores the closing price of the issue as an argument. --Returns three DataFrame type objects

Example: In the code lines 79 to 81 of Fig. 2,

macd = pd.DataFrame(data=0.0, columns=syms, index=dates)
macdsignal = pd.DataFrame(data=0.0, columns=syms, index=dates)
macdhist = pd.DataFrame(data=0.0, columns=syms, index=dates)

Define a DataFrame to store the return value. For statement on line 85

for (sym,val) in cp.items():
      macd[sym],macdsignal[sym],macdhist[sym] = ta.MACD(cp[sym])

Take the macd, macdsignal, macdhist of each brand in.

Lines 90 and 91 determine the golden cross and dead cross of the MACD line and MACD signal line.

macd_golden = (macd > macdsignal) & (macd.shift(1) < macdsignal.shift(1))
macd_dead = (macd < macdsignal) & (macd.shift(1) > macdsignal.shift(1))

Determine buy and sell signals from MACD and RSI

buy_sig = macd_golden | (rsi < 30) #Golden cross and oversold
sell_sig = macd_dead | (rsi > 70) #Dead cross and overbought

In this way, talib makes it easy to implement well-known technical indicators such as MACD and RSI.

Summary

This time, I picked up the methods that are frequently used in creating algos for talib and pandas. I hope you read this and get a better understanding of QuantX Factory's algorithms. Thank you for reading the poor text until the end.

Disclaimer Precautions

Please note that we are not responsible for any profit or loss caused by actual transactions using this code / knowledge.

Recommended Posts