価格の波に乗ろう! SOLっていうコインで試す、ちょっと特別な取引方法
この説明では、「SOL」という仮想通貨(暗号資産)を5分ごとに売り買いするときの、ある作戦についてお話しします。専門的な言葉は使わずに、誰にでも分かるように説明しますね。この作戦はうまくいかなかったのですが、どうして失敗したのかを知ることで、取引のヒントが見つかるかもしれません。
導入と前提条件
この説明では、「SOL」という仮想通貨(暗号資産)を5分ごとに売り買いするときの、ある作戦についてお話しします。専門的な言葉は使わずに、誰にでも分かるように説明しますね。この作戦はうまくいかなかったのですが、どうして失敗したのかを知ることで、取引のヒントが見つかるかもしれません。
【検証】戦略のバックテスト概要
- 戦略名: Hilbert Transform を使用したトレンド追従戦略
- 対象銘柄: SOL/USDT
- 時間足: 5m
- 期間: 2024-10-22〜2025-08-25(306日間)
- 初期資金: $10,000
- 手数料・スリッページ: 0.1% / 0.1%
- 取引所: bybit
Hilbert Transform の理論的背景
世の中のものの値段は、いつも同じペースで動くわけではありません。急に上がったり、ゆっくり下がったり、行ったり来たりを繰り返す波のような動きをします。「ヒルベルト変換」は、そんな複雑な波の動きをくわしく分析するための方法です。価格が上がる勢いがあるか、下がる勢いがあるかを計算で探して、売り買いのベストなタイミングを見つけるための道具になるはずなんです。難しい計算は全部コンピューターがやってくれるので、私たちはその結果を見るだけで大丈夫ですよ。
具体的な売買ルール(今回の検証)
エントリー条件
- グラフに「サイン線」と「リード線」という2本の線があります。サイン線がリード線を下から上に追い抜いたときが「買い」の合図です。
- 1つ前の時点ではサイン線がリード線より下だったのに、今、上になったときも「買い」の合図です。
エグジット条件
- サイン線がリード線を上から下に追い抜いたときが「売って手じまい」する合図です。
- 1つ前の時点ではサイン線がリード線より上だったのに、今、下になったときも「売って手じまい」する合図です。
リスク管理
大きな損をしないように、1回の取引で失ってもいい金額をあらかじめ決めておきます。例えば、「持っているお金の2%まで」という感じです。もし予想と反対に価格が動いて損が出始めたら、すぐに売って取引を終えることで、それ以上損が大きくならないようにします。
再現手順(HowTo)
- Python/依存(ccxt, pandas, ta)をインストール
- ccxtでSOL/USDTのOHLCVを取得して前処理
- 『Hilbert Transform』に必要な指標を算出(ta 等)
- 閾値・クロス条件から売買シグナルを生成
- 手数料・スリッページを加味して検証・評価
【結果】パフォーマンス
価格の推移
資産の推移
パフォーマンス指標
指標 | 値 |
---|---|
総トレード数 | 6951回 |
勝率 | 18.85% |
平均利益 | 0.48% |
平均損失 | -0.59% |
期待値 | -0.39% |
プロフィットファクター | 0.18 |
最大ドローダウン | 100% |
最終リターン | -100% |
シャープレシオ | -2.29 |
HODL(Buy&Hold) | 20.86% |
HODL戦略との比較
実装コード(Python)
"""
Hilbert Transform Trading Signal Generator
ヒルベルト変換を使用してサイクルとトレンドを検出
"""
import pandas as pd
import numpy as np
def calculate_hilbert_signals(df: pd.DataFrame,
period: int = 7) -> pd.DataFrame:
"""
Hilbert Transform戦略のシグナル生成
Parameters:
-----------
df : pd.DataFrame
OHLCVデータ
period : int
基本期間(デフォルト: 7)
Returns:
--------
pd.DataFrame
シグナルが追加されたDataFrame
"""
df = df.copy()
# Weighted Close Price
df['wcp'] = (df['high'] + df['low'] + 2 * df['close']) / 4
# Smooth (4期間WMA)
df['smooth'] = (4 * df['wcp'] + 3 * df['wcp'].shift(1) +
2 * df['wcp'].shift(2) + df['wcp'].shift(3)) / 10
# Detrender (高周波成分の除去)
df['detrender'] = (0.0962 * df['smooth'] + 0.5769 * df['smooth'].shift(2) -
0.5769 * df['smooth'].shift(4) - 0.0962 * df['smooth'].shift(6)) * 0.075
df['detrender'] = df['detrender'].shift(3)
# Compute InPhase and Quadrature components
df['q1'] = (0.0962 * df['detrender'] + 0.5769 * df['detrender'].shift(2) -
0.5769 * df['detrender'].shift(4) - 0.0962 * df['detrender'].shift(6)) * 0.075
df['i1'] = df['detrender'].shift(3)
# Advance Phase components
df['ji'] = (0.0962 * df['i1'] + 0.5769 * df['i1'].shift(2) -
0.5769 * df['i1'].shift(4) - 0.0962 * df['i1'].shift(6)) * 0.075
df['jq'] = (0.0962 * df['q1'] + 0.5769 * df['q1'].shift(2) -
0.5769 * df['q1'].shift(4) - 0.0962 * df['q1'].shift(6)) * 0.075
# Phasor components
df['i2'] = df['i1'] - df['jq']
df['q2'] = df['q1'] + df['ji']
# Smooth the components
df['i2'] = 0.2 * df['i2'] + 0.8 * df['i2'].shift(1).fillna(0)
df['q2'] = 0.2 * df['q2'] + 0.8 * df['q2'].shift(1).fillna(0)
# Homodyne Discriminator
df['re'] = df['i2'] * df['i2'].shift(1) + df['q2'] * df['q2'].shift(1)
df['im'] = df['i2'] * df['q2'].shift(1) - df['q2'] * df['i2'].shift(1)
# Smooth
df['re'] = 0.2 * df['re'] + 0.8 * df['re'].shift(1).fillna(0)
df['im'] = 0.2 * df['im'] + 0.8 * df['im'].shift(1).fillna(0)
# Compute Period
df['period'] = 2 * np.pi / (np.arctan2(df['im'], df['re'] + 0.0001) + 0.0001)
df['period'] = df['period'].clip(period * 0.67, period * 1.5)
df['period'] = 0.2 * df['period'] + 0.8 * df['period'].shift(1).fillna(period)
# Compute Phase
df['phase'] = np.arctan2(df['q1'], df['i1'] + 0.0001) * 180 / np.pi
# Sine and Lead Sine
df['sine'] = np.sin(df['phase'] * np.pi / 180)
df['lead_sine'] = np.sin((df['phase'] + 45) * np.pi / 180)
# シグナル生成
df['sine_prev'] = df['sine'].shift(1)
df['lead_sine_prev'] = df['lead_sine'].shift(1)
df['is_buy'] = (
(df['sine'] > df['lead_sine']) &
(df['sine_prev'] <= df['lead_sine_prev'])
) & df['sine'].notna()
df['is_sell'] = (
(df['sine'] < df['lead_sine']) &
(df['sine_prev'] >= df['lead_sine_prev'])
) & df['sine'].notna()
# 不要カラム削除
drop_cols = ['wcp', 'smooth', 'detrender', 'q1', 'i1', 'ji', 'jq', 'i2', 'q2',
're', 'im', 'phase', 'sine_prev', 'lead_sine_prev']
df.drop(drop_cols, axis=1, inplace=True, errors='ignore')
return df
なぜこの結果になったのか(3つの理由)
- 1この作戦の勝率は約19%と低く、平均すると1回の取引で少しずつ損をする計算になっていました。
- 2「もうけ」と「損」のバランスを示す数字(PF)が0.18と、とても低かったです。これは、もうけよりも損がずっと多かったことを意味します。
- 3最終的に、最初のお金が全部なくなってしまいました(最終リターン-100%)。また、取引の途中で、一時的にお金がゼロになるくらいまで減ってしまう場面がありました(最大ドローダウン100%)。
この結果から学べる3つの教訓
- 1この作戦は、過去のデータで試したところ、残念ながら利益を出すことができませんでした。なので、このまま使うのは危ないということが分かりました。
- 2計算上は面白そうな方法でも、実際にやってみて利益が出ないと意味がない、という大切な教訓になりました。
- 3勝率が低くても、1回勝ったときの利益が、負けたときの損失よりずっと大きければトータルでプラスになります。でも、この作戦は逆のパターンになっていたようです。
リスク管理の具体的手法
取引量の決め方
1回の取引で使うお金の量は、もし負けても全体のお金が大きく減らないように、あらかじめ決めておきます。例えば、全部のお金の1%とか2%とか、少ない量にするのが基本です。
損失が大きくなったときの対処法
もしも損が続いて、全体のお金が一定の割合(例えば10%)以上減ってしまったら、一度取引をお休みします。そして、作戦を見直したり、市場の様子が変わるのを待ったりします。
資金管理の方法
持っているお金の全部を取引に使うのではなく、一部だけを使います。残りは現金などで持っておき、万が一の事態にそなえることが大切です。
改良案の具体的提案
- 計算に使う数字の設定(パラメータ)を色々と変えてみて、もっと良い結果が出ないか試してみる必要があります。
- 取引の回数をへらして、「これは!」という確信が持てるタイミングだけで取引するように、ルールをもっと厳しくしてみます。
- 他の分析ツール(例えば移動平均線など)と組み合わせて、「買い」や「売り」の合図が本物かどうかを確かめる工夫をします。これで、「だまし」の合図に引っかかるのを減らせるかもしれません。
実用性の向上(運用上の注意)
- この作戦は、このまま使うとお金を失う可能性がとても高いです。もし試すなら、まずはお小遣いのような本当に少ない金額でやるか、ゲームのお金で練習(シミュレーション)をたくさんしてください。
- 5分ごとの取引は、値動きが速くてドキドキしやすいです。感情的にならずに、決めたルールを冷静に守ることがとても大事ですよ。
- マーケットの状況はいつも変わっていきます。だから、この作戦がずっとうまくいくとは限りません。定期的に成績をチェックして、必要なら作戦を見直しましょう。
検証の透明性と信頼性
- データの出所: この結果は、2024年10月から2025年8月までの「SOL/USDT」の過去の価格データを使った、練習試合(バックテスト)にもとづいています。
- 検証のやり方: 取引回数、勝率、もうけと損のバランス(PF)、最終的なもうけ、最大でどれくらいお金が減ったか(最大DD)などの成績データを見て、作戦がうまくいったかを確かめています。
- コード: この取引ルールをコンピューターで動かすためのプログラム(Pythonコード)が公開されています。
- 注意事項: この説明は、過去のデータにもとづいたものです。未来でも同じように利益が出ることを保証するものではありません。投資には、お金が減ってしまうリスクが必ずあります。自分のお金を使うときは、自分の判断と責任で行ってくださいね。