テクニカル指標(RSI,MACD,ボリンジャーバンド)を使って、ビットコインFX@bitflyerの自動売買プログラムを走らせてみた。こんな単純なシステムトレードでも、実装してみると意外と大変!
エヴァのマギシステムみたいに、テクニカル指標(RSI,MACD,ボリンジャーバンド,ATR)の内、3つのシグナルが出たら売買注文(成行)を出す投票式にしてみたけど
1分足でも、なかなかそんな都合のよいタイミングが出てこない…。
まあ、一週間ぐらい様子を見てみるか~。
実際に数日間、自動売買してみると、テクニカル指標通りには反転せずに、そのままブレイクアウトする事も多かった。
だいたいマイナス数百円で2時間タイムアウトで損切り状態になる。
あと、entryとexitが同じロジックなので、狙った方向に思いっきり良い感じに伸びている時でも、反転狙いロジックなので逆注文が入って終了!とかあって悲しかった…。
感覚的には、プラスマイナスゼロな結果だった。エントリー処理は良さげなので、利確1%損切り1%+2時間だけにしておいて、出口戦略は人間がやっても良いかも…。半自動売買(苦笑)
売買ストラテジー
1, 売買する単位は最低売買単位0.01BTCのみ。レバレッチはかけない(ロスカット無し)
2, 買いシグナルが出たら買い、売りシグナルが出たら売り、でエントリーしてポジションを持つ。
※現物と違って、FXやCFDは信用取引なので売りエントリーが出来る
3, ポジションを持って2時間以上経過したら逆注文で精算。
4, 1%以上損失が出たらロスカットする
5, 1%以上の利益が出たら利確する
6, 2時間以内に、持っているポジションと逆注文の売買シグナルが出たら注文して利確。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
import pandas as pd import ta import pybitflyer import config import time from datetime import datetime, timedelta, timezone # bitFlyer APIインスタンスの作成 api = pybitflyer.API(api_key=config.API_KEY, api_secret=config.API_SECRET) # ログファイルの設定 log_file = 'trading_log.txt' ohlcv_file = 'ohlcv_data.csv' def log_message(message): """メッセージをコマンドラインとログファイルに出力""" print(message) with open(log_file, 'a') as f: f.write(f"{message}\n") # 約定データをクロールしてOHLCVデータを計算 def fetch_and_aggregate_executions(): executions = [] count = 0 while len(executions) < 5000: if executions: last_id = executions[-1]['id'] new_executions = api.executions(product_code="FX_BTC_JPY", count=500, before=last_id) else: new_executions = api.executions(product_code="FX_BTC_JPY", count=500) if not new_executions: break executions.extend(new_executions) count += len(new_executions) if not executions: raise ValueError("No data received from API") df = pd.DataFrame(executions) df['timestamp'] = pd.to_datetime(df['exec_date'], errors='coerce', utc=True) df.dropna(subset=['timestamp'], inplace=True) # 'timestamp'がNaTの行を削除 df.set_index('timestamp', inplace=True) ohlcv = df['price'].resample('10s').ohlc() ohlcv['volume'] = df['size'].resample('10s').sum() ohlcv.dropna(inplace=True) ohlcv.to_csv(ohlcv_file) return ohlcv # テクニカル指標の計算 def calculate_indicators(df): df['sma'] = ta.trend.sma_indicator(df['close'], window=20) df['rsi'] = ta.momentum.rsi(df['close'], window=14) df['macd'] = ta.trend.macd(df['close']) df['macd_signal'] = ta.trend.macd_signal(df['close']) bb = ta.volatility.BollingerBands(df['close']) df['bb_upper'], df['bb_middle'], df['bb_lower'] = bb.bollinger_hband(), bb.bollinger_mavg(), bb.bollinger_lband() df['atr'] = ta.volatility.average_true_range(df['high'], df['low'], df['close'], window=14) return df # bitFlyer APIでの売買関数 def place_order(side, size): log_message(f"[{datetime.now(timezone.utc)}] Placing {side} order for {size} BTC...") order = api.sendchildorder( product_code="FX_BTC_JPY", child_order_type="MARKET", side=side, size=size, minute_to_expire=10000, time_in_force="GTC" ) log_message(f"[{datetime.now(timezone.utc)}] Order response: {order}") return order # 現在の評価証拠金を取得 def get_collateral(): collateral = api.getcollateral() log_message(f"[{datetime.now(timezone.utc)}] API response: {collateral}") jpy_collateral = collateral["collateral"] open_position_pnl = collateral["open_position_pnl"] return jpy_collateral, open_position_pnl # 現在のポジションを取得 def get_position(): positions = api.getpositions(product_code="FX_BTC_JPY") if positions: position = positions[0] return position["side"], position["size"], position["price"], pd.to_datetime(position["open_date"], utc=True) return None, 0, 0, None # メイン関数 def main(): min_trade_btc = 0.01 position_open_time = None max_hold_time = timedelta(hours=2) # ポジションを持つ最大時間 target_profit_ratio = 0.01 # 利益目標、エントリーして1%で利確 stop_loss_ratio = -0.01 # 損切りライン1% while True: try: df = fetch_and_aggregate_executions() df = calculate_indicators(df) if len(df) < 20: # データポイント数が20未満の場合 log_message(f"[{datetime.now(timezone.utc)}] Not enough data. Waiting for 60 seconds...") time.sleep(60) # データ不足時には60秒待機 continue latest_data = df.iloc[-1] rsi = latest_data['rsi'] macd = latest_data['macd'] macd_signal = latest_data['macd_signal'] bb_lower = latest_data['bb_lower'] bb_upper = latest_data['bb_upper'] close_price = latest_data['close'] atr = latest_data['atr'] log_message(f"[{datetime.now(timezone.utc)}] ATR: {atr}, RSI: {rsi}, MACD: {macd}, MACD Signal: {macd_signal}, Bollinger Bands Lower: {bb_lower}, Upper: {bb_upper}, Close Price: {close_price}") buy_signals = 0 sell_signals = 0 # 買いシグナル判定 if rsi < 30: buy_signals += 1 if macd > macd_signal: buy_signals += 1 if close_price < bb_lower: buy_signals += 1 if atr > df['atr'].mean(): # ボラティリティが平均より高い場合 buy_signals += 1 # 売りシグナル判定 if rsi > 70: sell_signals += 1 if macd < macd_signal: sell_signals += 1 if close_price > bb_upper: sell_signals += 1 if atr > df['atr'].mean(): # ボラティリティが平均より高い場合 sell_signals += 1 position_side, position_size, entry_price, position_open_time = get_position() log_message(f"[{datetime.now(timezone.utc)}] Current position: {position_side} {position_size} BTC") jpy_collateral, open_position_pnl = get_collateral() log_message(f"[{datetime.now(timezone.utc)}] Current collateral: {jpy_collateral} JPY, Open Position PnL: {open_position_pnl} JPY") # 利益目標達成時またはホールド時間経過時のポジションクローズ if position_side and position_size > 0: current_time = datetime.now(timezone.utc) hold_time = current_time - position_open_time profit_ratio = (close_price - entry_price) / entry_price if position_side == "BUY" else (entry_price - close_price) / entry_price if hold_time > max_hold_time or profit_ratio >= target_profit_ratio or profit_ratio <= stop_loss_ratio: close_side = 'SELL' if position_side == 'BUY' else 'BUY' order = place_order(close_side, position_size) if order.get('child_order_acceptance_id'): log_message(f"[{datetime.now(timezone.utc)}] Position closed: {position_size} BTC at {close_price} JPY after holding for {hold_time}") position_open_time = None continue # 購入ロジック if buy_signals >= 3 and position_side != "BUY": if position_side == "SELL": order = place_order('BUY', position_size) if order.get('child_order_acceptance_id'): log_message(f"[{datetime.now(timezone.utc)}] Close Sell position: {position_size} BTC at {close_price} JPY") time.sleep(300) # 少し待機してから新規ポジションを持つ continue btc_to_buy = min_trade_btc order = place_order('BUY', btc_to_buy) if order.get('child_order_acceptance_id'): log_message(f"[{datetime.now(timezone.utc)}] Buy order placed: {btc_to_buy} BTC at {close_price} JPY") position_open_time = datetime.now(timezone.utc) continue else: log_message(f"[{datetime.now(timezone.utc)}] Buy order failed.") # 売却ロジック if sell_signals >= 3 and position_side != "SELL": if position_side == "BUY": order = place_order('SELL', position_size) if order.get('child_order_acceptance_id'): log_message(f"[{datetime.now(timezone.utc)}] Close Buy position: {position_size} BTC at {close_price} JPY") time.sleep(300) # 少し待機してから新規ポジションを持つ continue btc_to_sell = min_trade_btc order = place_order('SELL', btc_to_sell) if order.get('child_order_acceptance_id'): log_message(f"[{datetime.now(timezone.utc)}] Sell order placed: {btc_to_sell} BTC at {close_price} JPY") position_open_time = datetime.now(timezone.utc) continue else: log_message(f"[{datetime.now(timezone.utc)}] Sell order failed.") time.sleep(60) # 1分待機して次のデータポイントへ except Exception as e: log_message(f"[{datetime.now(timezone.utc)}] Error: {e}") log_message(f"[{datetime.now(timezone.utc)}] Waiting for 60 seconds before retrying...") time.sleep(60) # エラー発生時には60秒待機して再試行 if __name__ == "__main__": main() |