移動平均線を使ったトレードプログラム例

移動平均線を使ったトレードプログラム例を紹介します。

移動平均線を使ったトレードプログラムについての説明

価格情報は、bitFlyer APIから取得したものを
ticksテーブルに保存していますので、
ここから取得します。

移動平均線の計算方法ですが、
単純移動平均であれば指定期間での
価格の平均値で求めることができます。

ここでは、SQLで価格を取得する際に

SELECT avg(best_bid) AS avg_bid FROM ticks

で平均値を算出しています。

WHERE節で対象とする期間を絞り込みました。

短期、中期、長期の移動平均をそれぞれ
short_avg, middle_avg, long_avgとして求め
これを比較しています。

エントリーの条件は、

if short_avg >= middle_avg and middle_avg >= long_avg:

の時に買い、

if short_avg <= middle_avg and middle_avg <= long_avg:

の時に売りとしています。

これに条件を付け加えるのであれば、
買いは現在値(bid)が短期線の上にある場合、
売りは現在値(bid)が短期線の下にある場合
というものでしょうか。

利確ラインと損切りラインをあらかじめ設定して
IFDOCOで注文を出しています。

サンプルプログラムを使用する際の注意点

移動平均線はテクニカル分析で最も一般的で
最もよく使われるインジケーターです。

それだけ使い勝手が良いインジケーターであると言えますが、
一点だけ注意点があります。

移動平均線は主にトレンドを検出するために使われます。
したがって、移動平均線を使ったトレードプログラムも
トレンドがある場合には良く機能しますが
トレンドがないレンジ相場ではパフォーマンスは期待できません。

トレンドが存在するのかを見極めた上で使用するようにしましょう。

移動平均線を使ったトレードサンプルプログラム例

import bitflyerApi
import pymysql
import datetime
import decimal
import configparser
import os
import sys

config = configparser.ConfigParser()
config.read(os.path.dirname(os.path.abspath(__file__)) + "/config.ini")

dbh = pymysql.connect(
         host=config.get('db', 'host'),
         user=config.get('db', 'user'),
         password=config.get('db', 'pass'),
         db=config.get('db', 'name'),
         charset='utf8',
         cursorclass=pymysql.cursors.DictCursor
    )


class trade:

    def __init__(self,account_id, param_id):
        self.account_id = account_id
        self.param_id = param_id

    def getParams(self):
        sql = "SELECT * FROM params WHERE account_id = %s AND id = %s" % (self.account_id, self.param_id)
        stmt = dbh.cursor()
        stmt.execute(sql)
        result = stmt.fetchone()
        stmt.close()

        if result != None:
            self.product_code = result['product_code']
            self.logic_id = result['logic_id']
            self.short_hour_period = result['short_hour_period']    # 1時間足の移動平均の期間・短期
            self.middle_hour_period = result['middle_hour_period']    # 1時間足の移動平均の期間・中期
            self.long_hour_period = result['long_hour_period']  # 1時間足の移動平均の期間・長期

            self.min_period = result['min_period'] # 1分足で最高値、最安値を取得する期間
            self.stop_period = result['stop_period']   # ストップに利用する最高値、最安値を取得する期間
            self.price_buffer = result['price_buffer']  # 最安値、最高値まで行かなくても注文を出せるようにする値幅
            self.size = float(result['size'])  # 注文サイズ
            self.min_size = float(result['min_size'])
            self.order_expire = result['order_expire']    #注文の有効期限(分)
            self.position_expire = result['position_expire'] # 建玉の有効期限(時間) これ以上ポジションを持っている場合は強制決済する

    def get_avg_ticks(self,fromtime, totime):
        sql = "SELECT avg(best_bid) AS avg_bid FROM ticks WHERE \
               timestamp >= '%s' AND timestamp <= '%s'" % (fromtime, totime)
        stmt = dbh.cursor()
        stmt.execute(sql)
        result = stmt.fetchone()
        stmt.close()
        return result['avg_bid']

    def get_ticks(self,fromtime, totime):
        sql = "SELECT max(best_bid) AS max_bid, min(best_bid) AS min_bid, max(best_ask) AS max_ask, min(best_ask) AS min_ask \
                FROM ticks WHERE timestamp >= '%s' AND timestamp <= '%s'" % (fromtime, totime)
        stmt = dbh.cursor()
        stmt.execute(sql)
        result = stmt.fetchone()
        stmt.close()
        return result

    def get_board(self):
        api = bitflyerApi.bitflyerApi(self.account_id)
        bids,asks = api.get_boards(self.product_code)

        bid_arr = []
        ask_arr = []

        i = 0
        for size in bids['size']:
            if size >= self.min_size:
                bid_arr.append(decimal.Decimal(bids['price'][i]))
            i += 1
        i = 0
        for size in asks['size']:
            if size >= self.min_size:
                ask_arr.append(decimal.Decimal(asks['price'][i]))

        if len(bid_arr) == 0 or len(ask_arr) == 0:
            sys.exit()

        return bid_arr[0],ask_arr[0]


    def bitflyer_data(self,log_data):
        data = {
                "order_method": "IFDOCO",
                "minute_to_expire": self.order_expire,
                "time_in_force": log_data['time_in_force'],
                "parameters":[{
                    "product_code": self.product_code,
                    "condition_type": log_data['order_type'],
                    "side": log_data['order_side'],
                    "price": str(log_data['price']),
                    "size": self.size,
                    },
                    {
                    "product_code": self.product_code,
                    "condition_type": log_data['order_type'],
                    "side": log_data['opposite_side'],
                    "price": str(log_data['price_profit']),
                    "size": self.size,
                    },
                    {
                    "product_code": self.product_code,
                    "condition_type": "STOP",
                    "side": log_data['opposite_side'],
                    "trigger_price": str(log_data['price_loss']),
                    "size": self.size,
                    }]
                }
        return data

    def execOrder(self,log_data):
        api = bitflyerApi.bitflyerApi(self.account_id)
        data = self.bitflyer_data(log_data)
        print(data)
        result = api.porder(data)
        return result

    def execCOrder(self,log_data):
        api = bitflyerApi.bitflyerApi(self.account_id)
        data = self.bitflyer_data(log_data)
        print(data)
        result = api.order(data)
        return result

    def insert_order_result(self,data):
        sql = "INSERT INTO trade_orders (parent_order_acceptance_id, \
                product_code, order_type, order_side, price, size, \
                price_profit, price_loss, result, \
                message, state, param_history_id, \
                account_id, logic_id) \
                VALUES ('%s', \
                '%s', '%s', '%s', %s, %s, \
                %s, %s, %s, \
                '%s', '%s', %s, \
                %s, %s)" \
                 % (data['parent_order_acceptance_id'], \
                self.product_code, data['order_type'], data['order_side'], data['price'], self.size, \
                data['price_profit'], data['price_loss'], data['result'], \
                data['message'].replace("'",""), data['state'],data['param_history_id'], \
                self.account_id, self.logic_id)

        print(sql)

        stmt = dbh.cursor()
        stmt.execute(sql)
        dbh.commit()
        stmt.close()

    def dict_to_list(self,data):
        data_arr = []
        for price in data:
            data_arr.append(price['best_bid'])
        return data_arr

    def insert_close_order_result(self,data):
        sql = "INSERT INTO trade_orders (child_order_acceptance_id, \
                product_code, order_type, order_side, size, \
                result, message, state, account_id) \
                VALUES ('%s', \
                '%s', '%s', '%s',%s, \
                %s, '%s', '%s', %s)" \
                 % (data['child_order_acceptance_id'], \
                self.product_code, data['order_type'], data['order_side'], self.size, \
                data['result'], data['message'].replace("'",""), data['state'], self.account_id)

        stmt = dbh.cursor()
        stmt.execute(sql)
        dbh.commit()

        stmt.close()

    def order(self):
        self.getParams()

        now = datetime.datetime.utcnow() #世界標準時間で

        api = bitflyerApi.bitflyerApi(self.account_id)
        if api.api_secret == None:
            sys.exit()
        if self.product_code == None:
            sys.exit()

        orders = api.get_corders(self.product_code)      #未約定の注文を取得
        positions = api.get_positions(self.product_code) #建て玉の一覧を取得
        bid,ask = self.get_board()

        if len(orders) == 0 and len(positions) == 0:
            print("exec tradeTrandDips:",self.account_id)
            print("start trade:", now.strftime('%Y-%m-%d %H:%M:%S'))
            # 仕掛中の注文、建て玉がないので、オーダーを出せるか調べる

            short_time = now - datetime.timedelta(hours=self.short_hour_period)
            short_avg = self.get_avg_ticks(short_time.strftime('%Y-%m-%d %H:%M:%S'), now)

            middle_time = now - datetime.timedelta(hours=self.middle_hour_period)
            middle_avg = self.get_avg_ticks(middle_time.strftime('%Y-%m-%d %H:%M:%S'), now)

            long_time = now - datetime.timedelta(hours=self.long_hour_period)
            long_avg = self.get_avg_ticks(long_time.strftime('%Y-%m-%d %H:%M:%S'), now)

            long_signal = ''
            if short_avg >= middle_avg and middle_avg >= long_avg:
                long_signal = 'BUY'
            elif short_avg <= middle_avg and middle_avg <= long_avg:
                long_signal = 'SELL'

            if long_signal != '':
                beforemin = now - datetime.timedelta(minutes=self.min_period)
                bitflyer_datas = self.get_ticks(beforemin.strftime('%Y-%m-%d %H:%M:%S'), now)
                #print("bitflyer_datas:", bitflyer_datas)

                stopmin = now - datetime.timedelta(minutes=self.stop_period)
                stop_datas = self.get_ticks(stopmin.strftime('%Y-%m-%d %H:%M:%S'), now)

            log_data = {}
            if long_signal == 'BUY' and bitflyer_datas['min_bid'] + self.price_buffer >= bid:
                price_diff = bitflyer_datas['max_bid'] - bitflyer_datas['min_bid']
                # 買いシグナル
                log_data['order_side'] = 'BUY'
                log_data['opposite_side'] = 'SELL'
                log_data['price'] = bid
                log_data['price_profit'] = log_data['price'] + price_diff
                log_data['price_loss'] = stop_datas['min_bid']
            elif long_signal == 'SELL' and bitflyer_datas['max_ask'] - self.price_buffer <= ask:
                price_diff = bitflyer_datas['max_ask'] - bitflyer_datas['min_ask']
                # 売りシグナル
                log_data['order_side'] = 'SELL'
                log_data['opposite_side'] = 'BUY'
                log_data['price'] = ask
                log_data['price_profit'] = log_data['price'] - price_diff
                log_data['price_loss'] = stop_datas['max_ask']

            if 'order_side' in log_data:
                log_data['order_type'] = 'LIMIT'
                log_data['time_in_force'] = 'GTC'
                result = self.execOrder(log_data)

                log_data['message'] = str(result)
                log_data['result'] = True

                if 'parent_order_acceptance_id' not in result:
                    log_data['result'] = False
                    log_data['parent_order_acceptance_id'] = ''
                    log_data['state'] = ''
                else:
                    log_data['parent_order_acceptance_id'] = result['parent_order_acceptance_id']
                    log_data['state'] = 'new'
                log_data['param_history_id'] = self.param_id
                self.insert_order_result(log_data)

if __name__ == '__main__':
    argvs = sys.argv
    account_id = argvs[1]
    param_id = argvs[2]

    trade = trade(account_id, param_id)
    trade.order()