移動平均線の指標であるMACDを調べているのですが、計算式が良くわからないので、ネットで探して、以下のサイトのコードを使うようにし、トレードログに出力するようにしてみました。
参考 ZAIFで自動取引シミュレーションシステムQiita今回はビットコインとなります。取引所はzaif です。
スポンサードリンク
15秒間隔で市場価格を取得を想定しているため正しいMACDにはなっていないかもしれません。1分足に近くなるようにパラメータは以下のようのしています。
プログラムを動かしてみるとわかるのですが、必ずしも15秒間隔で取得できていません。APIがエラーになったり、応答時間が長くなったりしているためです。
- MACDのパラメータ
12EMA 1260/15 = 48
26EMA 2660/15 = 104
9SIGNAL 9*60/15 = 36 単純移動平均線は、上記に合わせて、104にしています。
情報収集が目的に作成しているプログラムです。
- プライベートAPIは動かないようにしているため、売買は指定された価格、コイン数で必ず約定されるように動きます。このため、本当の売買とは動きが違うことを認識しておいてください。
実際に売買動作させると、利確したいのにキャンセルになり、損切りに変わったりする場合があります。 - プログラム開始時点の資産はプログラム内で指定しています。
動作環境
NCU(4コア)に、ESXiを導入した仮想サーバーを持っているため、windows7(2仮想CPU/4GBメモリ) をインストールして常時稼働させています。リモートディスクトップは、高速リモートディスクトップ「Yule」を使っています。
python 3.6.4 は、Anaconda version 5.1.0 で導入しています。
スポンサードリンク
コード
注意
プライベートAPIはコメントアウトしています。テスト用部分を削除してAPIコメントアウトを生かすと売買することも可能ですが、使用する場合は自己責任でお願いします。key.jsonの書式は前回の記事を参照してください。

# coding=utf-8
import json
import time
import pprint
import codecs
import slackweb
import numpy as np
import datetime
from zaifapi import ZaifPublicApi
from zaifapi import ZaifTradeApi
from decimal import (Decimal)
from logger import error_logger
from logger import trade_logger
with codecs.open('config/key.json', 'r', 'utf-8') as f:
API_data = json.load(f)
WEBHOOK_URL = API_data["slack_webhook_url"]
slack = slackweb.Slack(WEBHOOK_URL)
API_KEY = API_data["zaif_key"]
API_SECRET = API_data["zaif_secret"]
COIN_NAME = 'btc'
COIN_PAIR = 'btc_jpy'
EXCHANGE = 'Zaif:'
trade_log = trade_logger.TradeLogger('zaif', COIN_NAME)
error_log = error_logger.ErrorLogger('error')
PERIOD1 = 12 # 短期平均線
PERIOD2 = 104 # 平均線&ボリンジャーバンドの間隔数
LOOP_TIME = 15 # 市場価格取得間隔の秒数
CANCEL_LOOP_MAX = 4 # キャンセルするまでのループ回数 4x15=60秒
TRADE_HOLD_MAX = 4 # トレード直後に売買しないようにする回数 4x15=60秒
last_price = [] # 市場価格
cancel_flag = False # キャンセル処理をするかのフラグ
cancel_loop_count = 0 # キャンセルまでのカウント
trade_hold_count = 0 # トレード後の売買処理しないカウント
low_borin1 = 0 # x1低ボリンジャーバンド
low_borin2 = 0 # x2低ボリンジャーバンド
moving_avg1 = 89999999 # 移動平均
moving_avg2 = 89999999 # 移動平均
sigma2 = 0 # 標準偏差
moving_avg1_pre = 0 # 一つ前の移動平均
moving_avg2_pre = 0 # 一つ前の移動平均
moving_avg1_flag = True
moving_avg2_flag = True
high_borin1 = 89999999 # x1高ボリンジャーバンド
high_borin2 = 89999999 # x2高ボリンジャーバンド
bid_amount = 0.0 # 買い取引量
ask_amount = 0.0 # 売り取引量
bid_depth_amount = 0.0 # 板買い
ask_depth_amount = 0.0 # 板売り
funds_jpy = 0 # 注文余力日本円
funds_coin = 0.0 # 注文余力コイン
start_funds_jpy = 0 # プログラム開始時の日本円
last_trade_func = '' # 取引機能
last_trade_order_id = 0 # 最後に取引したID
last_trade_size = 0.0 # 最後に取引したサイズ
last_trade_price = 0 # 最後に取引した価格
last_trade_price_pre = 0 # 一つ前の取引した価格(キャンセルした時用)
last_trade_type = 0 # 0:購入、1:利確1、2:利確2、3:損切り
bid_flag = False
asset_info = False
message_func = '' # トレードログ用
message_trade = '' # トレードログ用
message_size = 0 # トレードログ用
message_price = 0.0 # トレードログ用
date_time = datetime.datetime.now() # print用表示タイム
current_price = 0 # 一時的に保管する市場価格
END_FLAG = False # プログラムの終了タイム
end_datetime = datetime.datetime.now() # 終了タイム
END_HOUR = 16 # 終了時
END_MINUTE = 30 # 終了分
call_count = 0 # API呼び出し繰り返し回数
CALL_MAX = 6 # APIコール最大連続回数
call_success = False
##############################
# MACD 追加 約分足で想定
##############################
MACD9 = [0.00000]
SMA12 = [0.00000]
SMA26 = [0.00000]
EMA12 = [0.00000]
EMA26 = [0.00000]
LOOP_CNT9 = 36 # MACD9のための定数(15秒間隔なので36)
LOOP_CNT12 = 48 # EMA12のための定数(15秒間隔なので48)
LOOP_CNT26 = 104 # EMA26のための定数(15秒間隔なので104)
# ※α(平滑定数)=2 /(n+1)
# A12AAA = 2 / (12.0000 + 1.0000)
# A26AAA = 2 / (26.0000 + 1.0000)
A12AAA = 2 / (48.0000 + 1.0000)
A26AAA = 2 / (104.0000 + 1.0000)
SMA12_AVE = 0.0000
SMA26_AVE = 0.0000
MACD9_AVE = 0.0000
MACD = 0.0
MACD9_AVE_PRE = 0.0000
MACD_PRE = 0.0
for var in range(0, LOOP_CNT12):
SMA12.append(SMA12_AVE)
for var in range(0, LOOP_CNT26):
SMA26.append(SMA12_AVE)
for var in range(0, LOOP_CNT12):
EMA12.append(SMA12_AVE)
for var in range(0, LOOP_CNT26):
EMA26.append(SMA12_AVE)
for var in range(0, LOOP_CNT9):
MACD9.append(SMA12_AVE)
#####################################
# テスト用 #
user_coin_asset = 0.0 # 実売買しないのでテスト用コイン資産
user_jpy_asset = 5000.0 # 実売買しないのでテスト用日本円資産
# #
#####################################
def check_results(trade_result):
global last_trade_order_id, cancel_loop_count, cancel_flag
if trade_result['order_id'] != 0:
last_trade_order_id = trade_result['order_id']
cancel_flag = True
cancel_loop_count = 0
else:
print('■ 取引が完了しました。')
# slack.notify(text='取引完了')
last_trade_order_id = 0
if __name__ == '__main__':
zaif_public = ZaifPublicApi()
zaif_trade = ZaifTradeApi(API_KEY, API_SECRET)
init_flag = False
while not init_flag:
try:
last_trade_price = int(zaif_public.last_price(COIN_PAIR)["last_price"])
# trade_result = zaif_trade.get_info2()
# start_funds_jpy = trade_result['deposit']['jpy']
# if trade_result['deposit'][COIN_NAME] != 0:
# start_funds_jpy += trade_result['deposit'][COIN_NAME] * last_trade_price
#####################################
# テスト用 #
start_funds_jpy = user_jpy_asset
# #
#####################################
except:
continue
else:
init_flag = True
date_time = datetime.datetime.now()
end_datetime = datetime.datetime(date_time.year, date_time.month, date_time.day, END_HOUR,
END_MINUTE)
END_FLAG = False
bid_flag = False
if date_time > end_datetime:
end_datetime += datetime.timedelta(days=1)
print('END DATE TIME:', end_datetime)
profit_count = 0
loss_count = 0
while True:
message_func = ''
message_trade = ''
message_price = 0
message_size = 0.0
date_time = datetime.datetime.now()
start_time = time.time()
try:
# trade_result = zaif_trade.get_info2()
# funds_coin = Decimal(trade_result['funds'][COIN_NAME]).quantize(Decimal('0.0001'))
# funds_jpy = trade_result['funds']['jpy']
#####################################
# テスト用 #
funds_coin = user_coin_asset
funds_jpy = user_jpy_asset
# #
#####################################
current_price = int(zaif_public.last_price(COIN_PAIR)["last_price"])
result = zaif_public.trades(COIN_PAIR)
bid_amount = 0.0
ask_amount = 0.0
for currency in result:
if currency['trade_type'] == 'bid':
bid_amount += currency['amount']
if currency['trade_type'] == 'ask':
ask_amount += currency['amount']
result = zaif_public.depth(COIN_PAIR)
bid_depth_amount = 0
ask_depth_amount = 0
for currency in result['bids']:
bid_depth_amount += Decimal(float(currency[1])).quantize(Decimal('0.0001'))
for currency in result['asks']:
ask_depth_amount += Decimal(float(currency[1])).quantize(Decimal('0.0001'))
except:
error_log.write()
print("エラー:Cant get data")
time.sleep(1)
continue
else:
last_price.append(current_price)
if len(last_price) <= PERIOD2:
print("■ 現在の情報です", len(last_price))
print(str(date_time))
print("市場取引価格:" + str(last_price[-1]))
print(COIN_NAME + "資産:" + str(funds_coin))
print("jpy資産:" + str(funds_jpy))
print("最終取引価格:" + str(last_trade_price))
if len(last_price) <= LOOP_CNT26:
for var in range(LOOP_CNT12 - 1, 0, -1):
SMA12[var] = SMA12[var - 1]
for var in range(LOOP_CNT26 - 1, 0, -1):
SMA26[var] = SMA26[var - 1]
SMA12[0] = current_price
SMA26[0] = current_price
SUM12 = 0.0000
SUM26 = 0.0000
for var in range(0, LOOP_CNT12):
SUM12 += SMA12[var]
for var in range(0, LOOP_CNT26):
SUM26 += SMA26[var]
SMA12_AVE = SUM12 / LOOP_CNT12
SMA26_AVE = SUM26 / LOOP_CNT26
else:
########################EMAの算出#########################
for var in range(LOOP_CNT12 - 1, 0, -1):
EMA12[var] = EMA12[var - 1]
for var in range(LOOP_CNT26 - 1, 0, -1):
EMA26[var] = EMA26[var - 1]
# EMA = B + α( A - B )
SMA12_AVE = SMA12_AVE + (A12AAA * (current_price - SMA12_AVE)) # EMA12
SMA26_AVE = SMA26_AVE + (A26AAA * (current_price - SMA26_AVE)) # EMA26
print('EMA12:', SMA12_AVE)
print('EMS26:', SMA26_AVE)
EMA12[0] = SMA12_AVE
EMA26[0] = SMA26_AVE
########################EMAの算出#########################
########################MACDの算出########################
MACD_PRE = MACD
MACD9_AVE_PRE = MACD9_AVE
MACD = SMA12_AVE - SMA26_AVE # MACD = 基準線(EMA) - 相対線(EMA)
for var in range(LOOP_CNT9 - 1, 0, -1):
MACD9[var] = MACD9[var - 1]
MACD9[0] = MACD
SUM9 = 0.0000
for var in range(0, LOOP_CNT9):
SUM9 += MACD9[var]
MACD9_AVE = SUM9 / LOOP_CNT9
print('MACD:', MACD)
print('MACD9:', MACD9_AVE)
########################MACDの算出########################
try:
if len(last_price) < 2 or asset_info:
if funds_coin < 0.0001 and last_trade_order_id == 0:
message_text = EXCHANGE + '現在の資産: ' + str(date_time) + '\njpy資産:' \
+ str(funds_jpy) + '\n' + COIN_NAME + '資産:' + str(funds_coin) \
+ '\nProfit:' + str(profit_count) + ' Loss:' + str(loss_count) \
+ '\nGain:' + str(funds_jpy - start_funds_jpy)
else:
message_text = EXCHANGE + '現在の資産: ' + str(date_time) + '\njpy資産:' \
+ str(funds_jpy) + '\n' + COIN_NAME + '資産:' + str(funds_coin) \
+ '\nProfit:' + str(profit_count) + ' Loss:' + str(loss_count)
slack.notify(text=message_text)
except:
error_log.write()
print("エラー:slack call error")
else:
asset_info = False
if len(last_price) > PERIOD2:
print('■資産、平均線、ボリンジャーバンド、MACD', len(last_price))
print(str(date_time))
print(COIN_NAME + '資産:', str(funds_coin))
print('jpy資産:', str(funds_jpy))
print('市場価格:', str(current_price))
print('平均 :', str(moving_avg2))
print('高偏差:', str(high_borin2))
print('低偏差:', str(low_borin2))
print('MACD', str(MACD))
print('SIGNAL', str(MACD9_AVE))
print("最終取引価格:" + str(last_trade_price))
message_func = EXCHANGE + '資産、平均、標準偏差'
# コインを持っていて
# if (funds_coin >= 0.0001 and current_price > high_borin2
# and trade_hold_count > TRADE_HOLD_MAX):
if (funds_coin >= 0.0001 and (current_price * 0.997) > last_trade_price
and trade_hold_count > TRADE_HOLD_MAX):
if moving_avg1 > moving_avg1_pre and (current_price * 0.995) < last_trade_price:
print('上昇傾向なので保留')
message_func += '、上昇傾向'
else:
# 売却
amount = funds_coin
adjustment = 0
if last_price[-1] > last_price[-2]:
adjustment = 10
elif last_price[-1] == last_price[-2]:
adjustment = 5
else:
adjustment = 0
call_count = 0
call_success = False
while not call_success:
call_count += 1
if call_count > CALL_MAX:
break
try:
# trade_result = zaif_trade.trade(currency_pair=COIN_PAIR, action="ask",
# price=last_price[-1] - 5 + adjustment,
# amount=amount)
#####################################
# テスト用 #
time.sleep(1)
user_coin_asset -= float(amount)
user_jpy_asset += ((last_price[-1] - 5 + adjustment) * float(amount))
trade_result = {'order_id': 11111}
# #
#####################################
except:
error_log.write()
print("エラー:cant trade[ask_up]")
time.sleep(1)
else:
last_trade_func = 'ask'
last_trade_size = amount
last_trade_price_pre = last_trade_price
last_trade_price = last_price[-1] - 5 + adjustment
last_trade_order_id = trade_result['order_id']
last_trade_type = 1
trade_hold_count = 0
asset_info = True
call_success = True
profit_count += 1
check_results(trade_result)
if call_success:
print('■ 利確1売却申請を行いました。')
pprint.pprint(trade_result)
print("売却注文価格:" + str(last_price[-1] - 5 + adjustment))
print("売却注文量 :" + str(amount))
message_func += '、利確1'
message_trade = 'ask'
message_price = current_price - 5 + adjustment
message_size = amount
try:
message_text = EXCHANGE + '■ 利確1売却申請: ' + str(date_time) \
+ '\nPrice:' + str(current_price - 5 + adjustment) \
+ '\nSize:' + str(amount)
slack.notify(text=message_text)
except:
error_log.write()
print("エラー:Slack call error]")
# コインを持っていて、
elif (funds_coin >= 0.0001 and trade_hold_count > TRADE_HOLD_MAX
and MACD < MACD9_AVE):
amount = funds_coin
call_count = 0
call_success = False
while not call_success:
call_count += 1
if call_count > CALL_MAX:
break
try:
# 売却
# trade_result = zaif_trade.trade(currency_pair=COIN_PAIR, action="ask",
# price=last_price[-1] - 5, amount=amount)
#####################################
# テスト用 #
time.sleep(1)
user_coin_asset -= float(amount)
user_jpy_asset += ((last_price[-1] - 5) * float(amount))
trade_result = {'order_id': 33333}
# #
#####################################
except:
error_log.write()
print("エラー:cant trade[ask]")
time.sleep(1)
else:
last_trade_func = 'ask'
last_trade_size = amount
last_trade_price_pre = last_trade_price
last_trade_price = last_price[-1] - 5
last_trade_order_id = trade_result['order_id']
trade_hold_count = 0
asset_info = True
call_success = True
if last_trade_price_pre < last_trade_price:
profit_count += 1
last_trade_type = 2
else:
loss_count += 1
last_trade_type = 3
check_results(trade_result)
if call_success:
print('■ デットクロス売却申請を行いました。')
pprint.pprint(trade_result)
print("売却注文価格:" + str(last_price[-1] - 5))
print("売却注文量 :" + str(amount))
message_func += '、デットクロス'
message_trade = 'ask'
message_price = current_price - 5
message_size = amount
try:
message_text = EXCHANGE + '■ デットクロス売却申請: ' + str(date_time) \
+ '\nPrice:' + str(current_price - 5) + '\nSize:' \
+ str(amount)
slack.notify(text=message_text)
except:
error_log.write()
print("エラー:Slack call error]")
# コインを持っていて平均より2%低い場合に損切り
elif (funds_coin >= 0.0001 and trade_hold_count > TRADE_HOLD_MAX
and current_price < (last_trade_price * 0.998)):
amount = funds_coin
call_count = 0
call_success = False
while not call_success:
call_count += 1
if call_count > CALL_MAX:
break
try:
# 売却
# trade_result = zaif_trade.trade(currency_pair=COIN_PAIR, action="ask",
# price=last_price[-1] - 5, amount=amount)
#####################################
# テスト用 #
time.sleep(1)
user_coin_asset -= float(amount)
user_jpy_asset += ((last_price[-1] - 5) * float(amount))
trade_result = {'order_id': 44444}
# #
#####################################
except:
error_log.write()
print("エラー:cant trade[ask]")
time.sleep(1)
else:
last_trade_func = 'ask'
last_trade_size = amount
last_trade_price_pre = last_trade_price
last_trade_price = last_price[-1] - 5
last_trade_order_id = trade_result['order_id']
last_trade_type = 3
trade_hold_count = 0
asset_info = True
call_success = True
loss_count += 1
check_results(trade_result)
if call_success:
print('■ 損切売却申請を行いました。')
pprint.pprint(trade_result)
print("売却注文価格:" + str(last_price[-1] - 5))
print("売却注文量 :" + str(amount))
message_func += '、損切'
message_trade = 'ask'
message_price = current_price - 5
message_size = amount
try:
message_text = EXCHANGE + '■ 損切売却申請: ' + str(date_time) \
+ '\nPrice:' + str(current_price - 5) + '\nSize:' \
+ str(amount)
slack.notify(text=message_text)
except:
error_log.write()
print("エラー:Slack call error]")
# コインを最小単位持っていない場合で
elif (funds_coin < 0.0001 and trade_hold_count > TRADE_HOLD_MAX and last_trade_order_id == 0
and MACD > MACD9_AVE and MACD_PRE < MACD9_AVE_PRE):
amount = Decimal(funds_jpy * 0.9 / current_price).quantize(Decimal('0.0001'))
call_count = 0
call_success = False
while not call_success:
call_count += 1
if call_count > CALL_MAX:
break
try:
# trade_result = zaif_trade.trade(currency_pair=COIN_PAIR, action="bid",
# price=last_price[-1], amount=amount)
#####################################
# テスト用 #
time.sleep(1)
user_coin_asset += float(amount)
user_jpy_asset -= (last_price[-1] * float(amount))
trade_result = {'order_id': 88888}
# #
#####################################
except:
error_log.write()
print("エラー:cant trade[bid]")
else:
last_trade_func = 'bid'
last_trade_size = amount
last_trade_price_pre = last_trade_price
last_trade_price = last_price[-1]
last_trade_order_id = trade_result['order_id']
last_trade_type = 0
trade_hold_count = 0
asset_info = True
call_success = True
check_results(trade_result)
if moving_avg1_pre > moving_avg1 or moving_avg2_pre > moving_avg2:
bid_flag = False
if call_success:
print('■ 購入申請を行いました')
pprint.pprint(trade_result)
print("購入注文価格:" + str(last_price[-1]))
print("購入注文量 :" + str(amount))
message_func += '、購入'
message_trade = 'bid'
message_price = current_price
message_size = amount
try:
message_text = EXCHANGE + '■ 購入申請: ' + str(date_time) \
+ '\nPrice:' + str(current_price) + '\nSize:' + str(amount)
slack.notify(text=message_text)
except:
error_log.write()
print("エラー:slack error")
if cancel_flag and cancel_loop_count > CANCEL_LOOP_MAX:
try:
trade_info = zaif_trade.get_info2()
if trade_info["open_orders"] > 0:
print("■ キャンセルしました")
print(zaif_trade.cancel_order(order_id=last_trade_order_id))
trade_hold_count = TRADE_HOLD_MAX
last_trade_price = last_trade_price_pre
message_func += '、キャンセル'
message_text = EXCHANGE + '■ キャンセルしました'
slack.notify(text=message_text)
asset_info = True
if last_trade_type == 1 or last_trade_type == 2:
profit_count -= 1
elif last_trade_type == 3:
loss_count -= 1
last_trade_type = 0
else:
print("■ 約定しました。")
message_func += '、約定'
message_text = EXCHANGE + '■ 約定しました'
slack.notify(text=message_text)
asset_info = True
last_trade_order_id = 0
except:
error_log.write()
print("エラー:cant trade[info/cancel]")
else:
cancel_flag = False
if len(last_price) >= PERIOD2:
if len(last_price) != PERIOD2:
trade_log.write(
func=message_func,
coin_asset=funds_coin,
jpy_asset=funds_jpy,
market_price=current_price,
order_id=last_trade_order_id,
trade=message_trade,
last_trade_price=last_trade_price,
price=message_price,
size=message_size,
mean_line1=moving_avg1,
mean_line2=moving_avg2,
bid_amount=bid_amount,
ask_amount=ask_amount,
bid_depth_amount=bid_depth_amount,
ask_depth_amount=ask_depth_amount,
sigma2=sigma2,
macd=MACD,
signal=MACD9_AVE)
cancel_loop_count += 1
trade_hold_count += 1
if moving_avg1_pre < moving_avg1:
moving_avg1_flag = True
else:
moving_avg1_flag = False
moving_avg1_pre = moving_avg1
moving_avg1 = np.average(last_price[-PERIOD1:])
if moving_avg2_pre < moving_avg2:
moving_avg2_flag = True
else:
moving_avg2_flag = False
moving_avg2_pre = moving_avg2
moving_avg2 = np.average(last_price[-PERIOD2:])
sigma2 = np.std(last_price[-PERIOD2:], ddof=1)
high_borin1 = moving_avg2 + sigma2
high_borin2 = moving_avg2 + sigma2 * 2
low_borin1 = moving_avg2 - sigma2
low_borin2 = moving_avg2 - sigma2 * 2
if moving_avg1_pre < moving_avg1 or moving_avg2_pre < moving_avg2_pre:
bid_flag = True
if date_time > end_datetime:
END_FLAG = True
if funds_coin < 0.0001 and END_FLAG and last_trade_order_id == 0:
message_text = EXCHANGE + 'プログラム終了\n' + str(date_time) + '\njpy資産:' \
+ str(funds_jpy) + '\n' + COIN_NAME + '資産:' + str(funds_coin) \
+ '\nProfit:' + str(profit_count) + ' Loss:' + str(loss_count) \
+ '\nGain:' + str(funds_jpy - start_funds_jpy)
slack.notify(text=message_text)
exit()
end_time = time.time()
elpsed_time = end_time - start_time
if elpsed_time > LOOP_TIME:
elpsed_time %= LOOP_TIME
print('ake time over', str(LOOP_TIME), 'sec')
time.sleep(LOOP_TIME - elpsed_time)
Monappy: MBDQ39VHypMQwfyR8SshuHvfPNUz321F6B

モナゲ(tipmona)ってなに?
そもそもMonacoinってなに?
コメントを残す