最終プロジェクト:検証できるストラテジー
設定可能なストラテジーを組み立て、シグナル、取引、結果を調べます。
最終プロジェクトでは、移動平均、入力、条件、自作関数、注文、表示を1つのファイルにまとめます。すべての判断を、データと記述したルールまでさかのぼって確認できるプログラムが目標です。
コードを書く前に説明する
2本のEMAのクロスを調べ、3本目の長期SMAで、終値が平均より上にあるかも確認します。
ロングの条件は、短期EMAが長期EMAを上抜けし、終値がトレンドフィルターのSMAより上にあることです。長さは短期 < 長期 < トレンドの順にします。満たさなければ、エントリー注文を出しません。
EMAの下抜けで成行決済します。エントリーには、tick単位のストップと利益目標も付けます。固定数量1、手数料0.1%、スリッページ2 tickを使います。
以上が課題のルールです。特定の市場で利益が出るとは判断しません。確認には、本の固定したデモデータを使います。
プログラム全体
final_strategy.pyを作ります。
"""
@pyne edge
"""
from pynecore.lib import close, color, input, location, plot, plotshape, script, shape, strategy, ta
def lengths_are_ordered(fast: int, slow: int, trend: int) -> bool:
return fast < slow and slow < trend
@script.strategy(
"Filtered Crossing Strategy",
overlay=True,
initial_capital=10000,
default_qty_type=strategy.fixed,
default_qty_value=1,
pyramiding=1,
commission_type=strategy.commission.percent,
commission_value=0.1,
slippage=2,
)
def main(
fast_length: int = input.int(9, title="Fast EMA length", minval=1, maxval=200),
slow_length: int = input.int(21, title="Slow EMA length", minval=1, maxval=200),
trend_length: int = input.int(50, title="Trend SMA length", minval=1, maxval=200),
use_trend_filter: bool = input.bool(True, title="Use trend filter"),
stop_ticks: int = input.int(100, title="Stop distance (ticks)", minval=1),
target_ticks: int = input.int(200, title="Target distance (ticks)", minval=1),
):
fast_average = ta.ema(close, fast_length)
slow_average = ta.ema(close, slow_length)
trend_average = ta.sma(close, trend_length)
crosses_up = ta.crossover(fast_average, slow_average)
crosses_down = ta.crossunder(fast_average, slow_average)
valid_lengths = lengths_are_ordered(fast_length, slow_length, trend_length)
trend_passes = not use_trend_filter or close > trend_average
entry_signal = valid_lengths and crosses_up and trend_passes
if entry_signal:
strategy.entry("Long", strategy.long)
if crosses_down:
strategy.close("Long")
strategy.exit("Bracket", "Long", loss=stop_ticks, profit=target_ticks)
plot(fast_average, "Fast EMA", color=color.blue, linewidth=2)
plot(slow_average, "Slow EMA", color=color.orange, linewidth=2)
plot(trend_average, "Trend SMA", color=color.gray, linewidth=2)
plotshape(entry_signal, "Entry signal", style=shape.triangleup,
location=location.belowbar, color=color.green)保存してデモデータで実行します。3本の平均と、エントリー条件を表す緑の三角が現れます。実際のエントリーと決済は、ストラテジーの取引マーカーが示します。

すべての足で計算するもの
3本の平均と2つのクロスは、条件の前に実行します。フィルターでエントリーしない場合も計算するため、全ローソク足にわたって計算状態が進みます。
lengths_are_orderedは3つの数を調べて論理値を返す自作関数で、ローソク足間の独自の状態はありません。valid_lengthsは、現在の結果を後の式で使うために保持します。
trend_passesは、フィルターが無効か、終値がSMAより上なら真です。input.boolで切り替え可能な論理入力を作ります。フィルター無効時はnot use_trend_filterが真なので、2つ目の条件を調べる必要がありません。
フィルターを無効にしても、平均は計算して描画します。切り替えるのは、線の表示ではなくエントリー判定です。
長さの順序が正しくない場合
各入力の範囲は、それぞれ1から200という制限だけです。短期30、長期10とも指定できてしまい、課題の順序に反します。
そのため、確認用の関数で関係を調べます。順序が違っても線は描画しますが、entry_signalは真になりません。課題で定めた長さの関係を外れたまま、エントリーしないようにしています。
デバッガーでvalid_lengthsを直接確認できます。保存済み入力が不適切なら、実行全体で偽のままです。
シグナルが出ても取引になるとは限らない
緑の三角はエントリー条件を示します。ストラテジーはシミュレーターに注文を渡します。約定には、次の足があること、十分な資金、許容数量など、さらに条件がある場合があります。
pyramiding=1のため、すでにロングを保有していると、追加のロングは許可されない場合があります。シグナルの数と取引数を同じだと思わないでください。
先頭では、トレンド用の50本SMAがまだありません。フィルター有効時は、その平均より価格が上という条件を真にできず、エントリー条件も満たしません。
動作を確認する
まず既定値を使い、Propertiesで数量、費用、約定方法を確認します。以前保存した上書き設定が、ソースの設定を変更していることがあります。
緑のシグナルを1つ選びます。デバッガーかチャートの値で、現在と前の足の2つのEMAを調べます。クロス、SMAフィルター、長さの関係を確認してください。3つとも真である必要があります。
次にTradesで対応するエントリーを探し、どの足で、いくらで約定したか確認します。決済は、クロス、ストップ、目標のどれで説明できるかを調べます。
予想できるケースを試す
動作確認には、結果を前もって予想できる変更を使います。
| 変更 | 確認すること |
|---|---|
| 短期と長期を同じにする | 長さの確認により新しいエントリー印がない |
| フィルターを無効にする | クロスに加えてSMAより上の価格は不要になる |
| ストップと目標を広げる | 同じ値動きでも決済の足が変わり得る |
| 数量を変える | 価格シグナルは同じでも損益金額が変わり得る |
1回に1つだけ変えます。先に予想を書き、実際の結果を記録してください。合わなければ、デバッガーで最初に違う判断を探します。
結果を比較する
フィルターの有効・無効を比べるときは、データ、数量、費用、長さを同じにします。Performanceの資産曲線とTradesを合わせて見てください。取引の少なさや最後の未決済ポジションも、集計に影響します。
実際の過去データを使う場合、設定に使う期間と、別に確認する期間を、結果を見る前に決めておきます。確認期間を見てからパラメーターを調整すると、その期間も設定に使ったことになります。
入力データの先頭には、十分なウォームアップ用の足が必要です。ある期間だけを切り出して実行すると、移動平均とポジション状態を作り直します。そのため、同じ期間でも、長いデータの途中で実行した結果と、最初の部分が異なることがあります。
自分で試す
Use crossings exitスイッチを加えてください。有効なら下抜けによる決済を残し、無効ならストップと目標だけで決済します。下抜け自体は両方で計算します。
解答
main()のパラメーターに加えます。
use_crossing_exit: bool = input.bool(True, title="Use crossings exit"),決済の分岐です。
if use_crossing_exit and crosses_down:
strategy.close("Long")crosses_down = ta.crossunder(...)は条件の前に残します。strategy.exitは、スイッチの状態にかかわらず残します。
クロス決済を無効にすれば、EMAの下抜け後もポジションが残る場合があります。次のエントリーシグナルが出ても、既存ポジションがあるため、新しい取引は開かない場合があります。
自分の版を作る
別名でコピーし、加えたいルールを1つ文章にします。どのデータからどの足で判断し、どう手計算で確認し、どの入力で変えるかを説明してください。
最初は新ルールを独立した変数で計算します。エントリーや決済の条件に使う前に、描画するか、デバッガーで観察します。全体の損益を説明しようとする前に、計算そのものの誤りを探せます。
ソース、保存した設定、使ったデータがそろって実験を表します。残しておけば、実行を再現し、確認できます。