BTC-FXストラテジー「フロッグ」のduelbot版を無償公開
こんにちわ。DuelMaster@イナトレです。
ちょっと前にテクノファンダさん(@TechnoFunda0)がフロッグという TradingView ストラテジーを公開されました。
ありがたいことに、またもや本文の中で duelbot をご紹介いただきましたので duelbot 版のストラテジーを作成してみました。元々が bitFlyer 向けのストラテジーということもあり、BitMEX 版を前提として duelbot 版へと移植するに当たりパラメータのチューニングや部分的なカスタマイズも行いました。ストラテジー自体の説明は元々の記事の方をご参照ください。
duelbot 本体内蔵の関数を呼んでる部分が多々あるので、他の bot ではそのまま動きませんが、python コードということで他の bot に移植可能な部分もあるかと思います。ここで公開するコードは許可なく持っていって構いません(※オリジナルの著作権はテクノファンダさんにありますのでご留意ください)。
■DuelBot 版ストラテジーのコード
さて、以下コードです。ver310 ベータ6 向けのロジックとして書きました。duelbot をお使いの皆様は、これを機会に ver310 ベータ6 への移行を検討されてください。
オリジナルからの変更点は以下の点です
①BitMEX 向けにパラメータをチューニング
②ポジション増しロジックの導入
使い方はいつもと同じく strategy フォルダ配下に以下のコードをコピペしたファイルを作成いただき配置するだけです。面倒だと思いますのでコミュニティーの方でがファイルをダウンロードできるようにしておきます。
st_frog_duel.json
{
"==comment==" : "↓↓↓↓ ここから先は strategy 向けの config を記述する ↓↓↓↓",
"==comment==" : "↓ 一度の注文で使うロット数を資産額の割合で指定",
"LEVERAGE" : 3.0,
"==comment==" : "↓ 保持できるロット数を最大レバレッジで制限する",
"MAX_LEVERAGE" : 9.0,
"==comment==" : "↓ frog のパラメータ",
"SMA_TERM" : 23,
"KATAMUKI1" : 1.1,
"KATAMUKI2" : 1.05,
"IKIOI" : 2.1,
"GENSOKU" : 1.5,
"DD_IKIOI" : 1.0,
"MAX_FREE_BTC" : 50,
"==comment==" : "↓ 注文を成行か指値か選択。成行なら true を指定。指値使い場合は false",
"IS_USE_TAKER" : true,
"==comment==" : "↓ ここは変更しない",
"isBottomJson" : true
}
st_frog_duel.py
## ver3.1専用
###################################################################################
## DuelBot - st_frog_duel / ver180729
'''
【説明】
- @TechnoFunda0 さんから許可いただいた frog duelbot 版です。足=1800推奨
- https://sayatori-arbi.com/2018/07/20/graduateschool3/
【カスタマイズの方法】
- st_frog_duel.json でパラメータ調節してみてください
'''
###################################################################################
###################################################################################
## ★★★ ユーザが設定変更して良い変数は全て config に入れてください ★★★
###################################################################################
###################################################################################
## 定数定義
###################################################################################
REQUIRE_BOT_VERSION = 310
###################################################################################
## calcIndicator: 指標を生成
## input: -
## output: -
## アドバイス:他のTA指標を使いたい場合に、ここで解析してGLB_indicator変数に入れます
###################################################################################
def calcIndicator():
global GLB_indicator
global GLB_order_signal
## テクニカル分析変数の準備(指標はTVに合わせて数値ブレがないように一つ前の足で計算する)
candles = getCandles(GLB_config['candleTimeFrame'])
GLB_indicator['candles'] = candles
candle_c = np.array([ohlcv[C] for ohlcv in candles], dtype='f8')
candle_b = np.array([abs(ohlcv[O] - ohlcv[C]) for ohlcv in candles], dtype='f8')
open = candles[-1][O]
high = candles[-1][H]
low = candles[-1][L]
close = candles[-1][C]
GLB_indicator['SMA'] = (ta.SMA(candle_c, GLB_STconfig['SMA_TERM']))
GLB_indicator['DEV'] = (ta.STDDEV(candle_c, GLB_STconfig['SMA_TERM']))
GLB_indicator['BodyAvg_M'] = (ta.SMA(candle_b, 20))[-1]
GLB_indicator['BodyAvg_S'] = (ta.SMA(candle_b, 3))[-1]
## debug
logger.debug('Indicator OHLCV=' + str(candles[-1]))
logger.debug('Indicator SMA=' + str(GLB_indicator['SMA'][-1]))
logger.debug('Indicator DEV=' + str(GLB_indicator['DEV'][-1]))
GLB_indicator['EXIT_SIGNAL'] = False
GLB_order_signal = OrderSignal.NONE
###################################################################################
## createTradeSignal: 指標から売買サインを判断
## input: position
## output: -
###################################################################################
def createTradeSignal(position):
global GLB_indicator
global GLB_order_signal
last = GLB_tickers[-1]['last']
candles = GLB_indicator['candles']
open = candles[-1][O]
high = candles[-1][H]
low = candles[-1][L]
close = candles[-1][C]
ma = GLB_indicator['SMA']
dd = GLB_indicator['DEV']
body_m = GLB_indicator['BodyAvg_M']
body_s = GLB_indicator['BodyAvg_S']
ikioi = GLB_STconfig['IKIOI']
gensoku = GLB_STconfig['GENSOKU']
katamuki1 = GLB_STconfig['KATAMUKI1']
katamuki2 = GLB_STconfig['KATAMUKI2']
dd_ikioi = GLB_STconfig['DD_IKIOI']
buy = ma[-1] + dd[-1] * dd_ikioi < close and dd[-2] * katamuki1 < dd[-1] and close < candles[-2][C] and body_m * ikioi < body_s
sell = close < ma[-1] - dd[-1] * dd_ikioi and dd[-2] * katamuki1 < dd[-1] and candles[-2][C] < close and body_m * ikioi < body_s
exit = dd[-1] < dd[-2] * katamuki2 or body_s < body_m * (ikioi - gensoku)
if buy:
GLB_order_signal = OrderSignal.BUY
logger.info('Find a long position signal')
if sell:
GLB_order_signal = OrderSignal.SELL
logger.info('Find a short position signal')
if exit:
GLB_indicator['EXIT_SIGNAL'] = True
###################################################################################
## noPositionStrategy: ノーポジ時の戦略アルゴリズム
## input: position, balance, lastOrderTimestamp
## output: True / False
###################################################################################
def noPositionStrategy(position, balance, lastOrderTimestamp):
global GLB_indicator
global GLB_order_signal
last = GLB_tickers[-1]['last']
logger.debug('Judge no position logic')
## マスタークロックと現在時刻を判定して、トレードする足との時間軸を揃える(そうじゃないとTVの結果と全然異なる)
if not isSynchronizedWithMasterClock():
logger.debug('Do nothing because clock is out of sync')
return False
## 残高が足りなければ何もしない(初期ロットは指定の2倍でインする)
lot = getLotPerOrder(GLB_STconfig['LEVERAGE'], last, balance, None)
if lot < 1:
logger.warn('Order was canceled due to insufficient assets')
return False
## 売買チャンスあるか判定する
createTradeSignal(position)
## シグナルがないときは何もしない
if GLB_order_signal == OrderSignal.NONE:
return False
## シグナルがあればシグナルに応じた売買注文をだす
logger.debug('Try creating position: signal=' + str(GLB_order_signal))
newPosition(GLB_order_signal, lot, GLB_STconfig['IS_USE_TAKER'])
return True
###################################################################################
## positionStrategy:ポジション時の戦略アルゴリズム
## input: position, balance, lastOrderTimestamp
## output: True / False
###################################################################################
def positionStrategy(position, balance, lastOrderTimestamp):
global GLB_indicator
global GLB_order_signal
last = GLB_tickers[-1]['last']
logger.debug('Judge position logic')
## ポジションがなぜか見つからないときは何もしない(Mex側でロスカなど)
if position is None:
return False
## 売買チャンスあるか判定する
createTradeSignal(position)
## マスタークロックと現在時刻を判定して、トレードする足との時間軸を揃える(そうじゃないとTVの結果と全然異なる)
if not isSynchronizedWithMasterClock():
logger.debug('Do nothing because clock is out of sync')
return False
## 利確シグナル(基本プラマイ0以上を要求する)
if GLB_indicator['EXIT_SIGNAL']:
lot = abs(position['currentQty'])
logger.info('Try exit positiob: signal=' + str(GLB_order_signal) + ', lot=' + str(lot))
closePosition(position)
return True
## シグナルがないときは何もしない
if GLB_order_signal == OrderSignal.NONE:
return False
## 残高が足りなければ何もしない(初期ロットは指定の2倍でインする)
lot = getLotPerOrder(GLB_STconfig['LEVERAGE'], last, balance, None)
if lot < 1:
logger.warn('Order was canceled due to insufficient assets')
return False
## シグナルがあればシグナルに応じたポジション増し、ナンピンなど売買注文をだす
logger.debug('Try creating position: signal=' + str(GLB_order_signal))
newPosition(GLB_order_signal, lot, GLB_STconfig['IS_USE_TAKER'])
return False
###################################################################################
## losscutStrategy: ロスカット戦略アルゴリズム
## input: position, balance, lastOrderTimestamp
## output: True / False
###################################################################################
def losscutStrategy(position, balance, lastOrderTimestamp):
return False
###################################################################################
## getLotPerOrder: 最新の状態で出せるロット数を取得
## input: leverage, last, balance, position
## output: lot
###################################################################################
def getLotPerOrder(leverage, last, balance, position):
base_symbol = GLB_config['base_symbol']
price = last if GLB_config['api_symbol'] == 'XBTUSD' else 1 / last
lot = int(leverage * balance['free'][base_symbol] * price)
max_lot = int(GLB_STconfig['MAX_LEVERAGE'] * balance['total'][base_symbol] * price)
if position is not None and max_lot < abs(position['currentQty']) + lot:
lot = max_lot - abs(position['currentQty'])
lot = max(0, lot)
logger.debug('LotPerOrder=' + str(lot))
return lot
###################################################################################
## getLotPerAddingOrder: 買い増し使うロット数を取得
## input: last, balance, position
## output: lot
###################################################################################
def getLotPerAddingOrder(last, balance, position):
base_symbol = GLB_config['base_symbol']
if position is None:
return 0
price = last if GLB_config['api_symbol'] == 'XBTUSD' else 1 / last
lot = abs(position['currentQty'])
max_lot = int(GLB_STconfig['MAX_LEVERAGE'] * balance['total'][base_symbol] * price)
if max_lot < lot * 2:
lot = 0
lot = min(GLB_STconfig['MAX_FREE_BTC'] * price * 0.5, lot)
logger.debug('LotPerAddingOrder=' + str(lot))
return lot
■2018-01-026からのパフォーマンス
まずは TradingView での結果。オリジナルの PINE ロジックにおける 30 分足での性能です。レバレッジは 1 倍で検証しました。こちらには買い増しロジックは入れていません。
duelbot 改造ロジック版の結果
改造点は前述したとおりでパフォーマンス結果は以下のとおりです。同じくレバレッジ 1 倍でのバックテスト結果です。こちらは 83% 増の結果となりました。DDは最大でも6%未満と好成績だと言えるでしょう。
まだまだ全パラメータの最適値を求めたわけではありませんが、このへんでチューニング作業も終わりとさせていただきました。
■あとがき
今後もオリジナルロジックも公開していきますし、許可が頂けるものは移植し公開していきます。DuelBot が気になった方は是非購入のご検討をよろしくお願い致します。
■免責事項
最後にお約束事項ではございますが、本書はあくまで bot ロジックの技術紹介という位置づけになります。ご利用は読者様個人の判断により自己責任とさせていただき、いかなる損失や損害などの被害が発生したとしても、私はその責任を負いかねます旨、ご了承ください。