コンテンツにスキップ

アルゴリズム取引のためのPythonの基本

トレーダーはPythonを使用して、取引ロボット、テクニカルインジケーター、プラグイン、およびアルゴリズム取引やマニュアル操作を支援する他のツールを構築します。この記事では、Pythonでのコーディングの基本を説明します。

Pythonは、Web開発、データサイエンス、自動化、そして最近ではcTraderでのアルゴリズム取引に使用される人気の高い高水準の汎用プログラミング言語です。 シンプルなシグナルベースの取引ロボットから複雑なマルチインジケーター戦略まで、Pythonはプロセスをより速く、効率的に、そして管理しやすくします。

1分でPythonアルゴリズム!

  • 内蔵のPythonサポートとcTrader Algo APIへのシームレスなアクセスを体験し、cTraderはネイティブ統合を提供する唯一の主要プラットフォームです。 プラグイン、アダプター、または複雑な設定は必要ありません。
  • ロジックが組み立てやすくなるため、戦略が達成すべきことに集中できます。 迅速な反復、再利用可能なコンポーネント、スケーラブルなパフォーマンスを楽しめます。
  • 迅速に開発し、スマートにデバッグし、障壁なくコラボレーションできます。 あなたのコードは常に適応性があり、信頼性が高く、保守可能です。
  • 世界中の数千の無料/オープンソースライブラリ、詳細なチュートリアル、コードサンプル、強力なコミュニティサポートにアクセスして活用できます。

変数とデータ型

Pythonでは、変数は動的に型付けされるため、データ型を明示的に宣言する必要はありません。 インタプリタは、割り当てられた値に基づいて実行時に型を決定します。

変数は単に値を割り当てることで作成します:

1
2
3
4
price = 105.50         # float
enabled = True         # boolean
symbol = "EURUSD"      # string
orders = []            # empty list

一般的なデータ型

種類 説明
int 整数 5, 100, api.StepPips
float 小数 1.1234, 100.5, api.Symbol.Ask
bool ブール値 True, False
str 文字列 "EURUSD", api.Label
list アイテムのコレクション [1, 2, 3], [p for p in api.Positions]

cTraderアルゴリズムでは、変数は市場情報、取引決定、cBot設定に関連する重要なデータを格納します。 これらの変数は、計算された取引量、インジケーター値、ブールフラグ、シンボル名、ラベルなどを表すことがよくあります。

以下はその例です:

1
2
3
4
class GridcBot():
    def on_start(self):
        self.volumeInUnits = api.Symbol.QuantityToVolumeInUnits(api.VolumeInLots)
        self.enoughMoney = True

この場合、self.volumeInUnitsは注文を出すために使用されるfloatであり、self.enoughMoneyはグリッド戦略でポジションを開き続けるかどうかを制御するboolです。

クラスとオブジェクト

Pythonはオブジェクト指向プログラミング(OOP)言語であり、クラスとオブジェクトを通じてコードを整理するためのツールを提供します。これらは現実世界のエンティティと動作をモデル化します。 クラスはオブジェクトを作成するための青写真を定義し、オブジェクトはそのクラスのインスタンスであり、独自のデータと動作を持ちます。 主要な概念は以下の通りです:

  • クラスはclassキーワードを使用して定義されます。
  • __init__メソッド(コンストラクター)は、オブジェクトの状態を初期化するために一般的に使用されます。
  • インスタンスメソッドは動作を定義します。
  • 属性は各オブジェクトに固有のデータを保持します。

例えば、Carクラスを作成できます:

1
2
3
4
5
6
7
class Car:
    def __init__(self, color, speed):
        self.color = color
        self.speed = speed

    def accelerate(self):
        self.speed += 10

その後、Carクラスのオブジェクトを作成し、それらとやり取りできます:

1
2
3
my_car = Car("red", 50)
my_car.accelerate()
print(my_car.speed)  # This returns 60

cTraderのコンテキストにおいて

cTraderでは、cBot、インジケーター、プラグインは、そのすべてのロジックをカプセル化したクラスとして実装されています。 アルゴリズムは、シンボル、チャート、注文、インジケーターなどにアクセスを提供する特別なapiオブジェクトを使用してcTraderプラットフォームと相互作用します。

cTraderでSimplecBotクラスを次のように作成できます:

1
2
3
class SimplecBot():
    def on_start(self):
        api.Print("cBot started")

SimplecBotクラスには、取引ロボットが起動したときに一度だけ実行される単一のメソッド(on_start(self))が含まれています。 api.Print関数はログにメッセージを書き込み、オブジェクトメソッドが取引行動をどのようにカプセル化するかを示します。

クラスの各インスタンスは、OOPの原則を使用して自身のライフサイクルと取引決定を管理する自己完結型の取引アルゴリズムを効果的に表します。 標準的なcTrader cBotでは、クラスは取引高やインジケーターの結果などの内部状態データを保存し、いくつかのメソッドを使用して取引ロジックを管理する場合があります。

MACDクロスオーバーの例を考えてみましょう。これは、起動時にMACDインジケーターを初期化し、MACDクロスオーバーシグナルが発生したときに買い注文を出します:

1
2
3
4
5
6
7
class MACDCrossover():
    def on_start(self):
        self.macd = api.Indicators.MacdCrossOver(api.Source, api.LongCycle, api.ShortCycle, api.SignalPeriods)

    def on_bar_closed(self):
        if self.macd.MACD.Last(0) > self.macd.Signal.Last(0):
            api.ExecuteMarketOrder(TradeType.Buy, api.SymbolName, volume)

selfキーワード

クラス内では、selfはクラスのインスタンスを参照し、その変数とメソッドにアクセスを提供します。 selfは次のために使用されます:

  • インジケーターの結果、ポジションの取引高、設定値などのデータを保存します。
  • on_start()on_tick()on_bar_closed()などの異なるメソッド呼び出し間で内部状態を追跡します。

この例は、selfがどのように使用されるかを示しています:

1
2
3
4
class MACDCrossover():
    def on_start(self):
        self.volume = api.Symbol.QuantityToVolumeInUnits(api.VolumeInLots)
        self.macd = api.Indicators.MacdCrossOver(api.Source, api.LongCycle, api.ShortCycle, api.SignalPeriods)

この場合、self.volumeは取引に使用する計算された取引高を保存し、self.macdはMACDインジケーターオブジェクトを保存して、cBotのライフサイクル全体でその値にアクセスできるようにします。 selfがなければ、これらの変数はon_start()内でローカルになり、取引決定が行われる可能性のあるon_bar_closed()ではアクセスできません。

メソッドと関数

関数は、特定のタスクを実行するために使用される再利用可能なコードのブロックです。 関数がクラス内で定義されると、それらはメソッドと呼ばれ、selfパラメータを介してクラスの内部状態を操作します。 cTraderでのアルゴリズム取引のコンテキストでは、メソッドは市場のティック、バーのクローズ、初期化などの異なる条件下で取引アルゴリズムがどのように動作するかを定義します。

プラットフォームは、適切なタイミングで特別なメソッド(イベントハンドラーと呼ばれる)を自動的に呼び出します。例えば:

on_start(self) – ボットが初期化されたときに一度呼び出されます。

1
2
def on_start(self):
    api.Print("cBot started")

on_tick(self) – 各着信市場ティックで呼び出されます。

1
2
def on_tick(self):
    api.Print("Tick received")

on_bar_closed(self) – 新しいキャンドル/バーがクローズしたときに呼び出されます(時間ベースの戦略に理想的です)。

1
2
3
4
def on_bar_closed(self):
    if Functions.HasCrossedAbove(...):
        self.close_positions(TradeType.Sell)
        api.ExecuteMarketOrder(...)

カスタムまたはヘルパーメソッド

コードの書き換えや重複を避けるために、独自のメソッドを定義できます。 そのようなカスタムメソッドは、取引ロボットが再利用する可能性のある共通のロジックをカプセル化する必要があります。

以下のメソッドは、現在のcBotのラベルに関連付けられたすべてのオープンポジションを返し、ポジションを一貫して追跡またはクローズすることを容易にします。

1
2
def get_bot_positions(self):
    return api.Positions.FindAll(api.Label)

現在の価格とポジションのエントリー価格の間のpipsでの距離を計算する別のメソッドを考えてみましょう。これはグリッドベースの戦略での決定に重要です。

1
2
def get_distance_in_pips(self, position):
    return (position.EntryPrice - api.Symbol.Ask if position.TradeType == TradeType.Buy else api.Symbol.Bid - position.EntryPrice) / api.Symbol.PipSize

メソッドが定義されると、selfキーワードを使用して同じクラス内から呼び出すことができます。 このアプローチにより、コードを重複させることなく、ロジックをボット内のどこでも再利用できます。

例えば、get_bot_positions()を定義した後、別のメソッド内でそれを呼び出してすべてのポジションをクローズする場合があります:

1
2
3
4
def close_positions(self, tradeType):
    for position in self.get_bot_positions():   # Helper method call
        if position.TradeType == tradeType:
            api.ClosePosition(position)

ここで、self.get_bot_positions()はヘルパーメソッドを呼び出し、現在のボットラベルに関連付けられたポジションのみを返します。

同様に、get_distance_in_pips()メソッドを再利用して取引決定を行うことができます。 例えば、グリッド戦略では、市場が最後のポジションから十分に離れた場合にのみ新しい注文を開きたい場合があります:

1
2
3
4
5
6
7
def on_tick(self):
    grid_positions = self.get_bot_positions()
    if len(grid_positions) > 0:
        position_with_highest_pips = sorted(grid_positions, key=lambda pos: pos.Pips, reverse=True)[0]
        distance = self.get_distance_in_pips(position_with_highest_pips)  # Helper method call
        if distance >= api.StepPips:
            self.open_position()

その他の概念

名前空間とインポート

Pythonの名前空間は、関連する識別子(変数、関数、クラス)をグループ化することでコードを整理し、名前の衝突を避けるのに役立ちます。 インポートを使用すると、外部モジュール、ライブラリ、またはパッケージを取り込んで、Pythonの標準機能を超えた機能を拡張できます。

Pythonでは、標準モジュールまたはカスタムパッケージをインポートできます:

1
2
import math
print(math.sqrt(16))  # 4.0

特定の関数やクラスをインポートすることもできます:

1
2
from math import sqrt
print(sqrt(25))  # 5.0

PythonでcTraderのアルゴリズムを書く場合、インポートは少し異なります。なぜなら、cTraderは.NETベースのcTrader Algo APIと統合されているからです。 この設定では、Pythonコードが.NETアセンブリと相互作用できるようにするブリッジが含まれており、これが典型的なcBotが次のように始まる理由を説明しています:

1
2
3
4
5
6
7
8
9
import clr

clr.AddReference("cAlgo.API")

# Import cAlgo API types
from cAlgo.API import *

# Import trading wrapper functions
from robot_wrapper import *

ここで:

  • import clrは共通言語ランタイム(CLR)をロードし、Pythonが.NETライブラリと相互運用できるようにします。
  • clr.AddReference("cAlgo.API")は、すべての取引関連のクラス、メソッド、オブジェクトを含むcAlgo APIアセンブリを参照します。
  • from cAlgo.API import *はAPIからすべての型(例:TradeTypeColorPosition)を取り込みます。
  • from robot_wrapper import *は、一般的な取引操作を簡素化するヘルパー関数とラッパーをロードします。

名前空間システムにより、次のことが保証されます:

  • Pythonの組み込み名(例:print)がcTraderの名(例:api.Print)と衝突しない。
  • すべての取引固有のオブジェクト(api.Symbolapi.Indicatorsapi.ExecuteMarketOrder)がcTrader Algo API名前空間から来ており、整理され区別されています。
  • 独自のカスタムパッケージをインポートして、cTraderの組み込み型と一緒に機能を拡張できます。

ループ

ループを使用すると、一連の値に対して操作を繰り返したり、条件が満たされるまで操作を繰り返したりすることができます。 アルゴリズム取引では、ループはポジションの処理(例:クローズ、フィルタリング、更新)、複数の資産やインジケーターにわたる条件のチェック、グリッドやマーチンゲール戦略の実装(繰り返し注文が必要な場合)などによく使用されます。

Pythonでは、反復処理が簡単で、forループ(コレクションを反復処理するため)とwhileループ(条件が変わるまで繰り返すため)の両方をサポートしています。

ポジションをクローズするためのループを記述できます:

1
2
3
4
def close_positions(self, tradeType):
    for position in self.get_bot_positions():
        if position.TradeType == tradeType:
            api.ClosePosition(position)

この場合:

  • self.get_bot_positions()は、現在のcBotに属するすべてのポジションを返します。
  • forループは各ポジションを処理します。
  • 一致するポジションはapi.ClosePositionでクローズされます。

すべてのオープングリッドポジションを反復処理し、それらを1つずつクローズし、ポジションが残っているかどうかを再帰的にチェックする別のループを考えてみましょう(部分的にクローズされた場合の安全性のため)。

1
2
3
4
5
6
def close_grid_positions(self):
    for position in self.get_grid_positions():
        position.Close()

    if len(self.get_grid_positions()) > 0:
        self.close_grid_positions()

条件文

条件文は、Pythonで実行の流れを制御し、論理式に基づいてアルゴリズムが決定を下すことを可能にします。 条件文は戦略ロジックの基盤であり、いつエントリー、エグジット、またはポジションを管理するかを決定します。

Pythonは3つの主要な条件構文をサポートしています:

  • if – 条件が真の場合にコードを実行します。
  • elif (else if) – 最初の条件が偽の場合に別の条件をチェックします。
  • else – どの条件も満たされない場合のフォールバックを提供します。

このコードで表現されたMACDクロスオーバーの条件を考えてみましょう:

1
2
3
4
if self.macd.MACD.Last(0) > self.macd.Signal.Last(0):
    api.ExecuteMarketOrder(TradeType.Buy, api.SymbolName, self.volumeInUnits, api.Label, api.StopLossInPips, api.TakeProfitInPips)
elif self.macd.MACD.Last(0) < self.macd.Signal.Last(0):
    api.ExecuteMarketOrder(TradeType.Sell, api.SymbolName, self.volumeInUnits, api.Label, api.StopLossInPips, api.TakeProfitInPips)

この構造:

  • MACDラインがシグナルラインの上にある場合に買い注文を出します。
  • MACDラインがシグナルラインの下にある場合に売り注文を出します。
  • 明確なシグナルがない場合は取引をスキップします。

リスト

Pythonは、取引、価格、インジケーターなどのデータコレクションを扱うための強力で簡潔なツールを提供します。 最も一般的なものの2つは、リスト内包表記とsum()などの組み込み集計関数です。

例えば、numbersを反復処理し、偶数のみを保持するコードを次のように記述できます:

1
2
numbers = [1, 2, 3, 4, 5]
squares = [n**2 for n in numbers if n % 2 == 0]

リスト内包表記を使用して、現在のシンボルのポジションのみを選択し、マルチシンボル環境で有用なフィルタリングされたリストを生成できます。

1
positions = [p for p in api.Positions if p.SymbolName == api.SymbolName]

sum()を使用して利益やボリュームを集計することは、グリッド取引戦略で総利益が目標に達したかどうかを判断するための標準的なアプローチです。

1
net_profit_sum = sum([p.NetProfit for p in grid_positions])

フィルタリングと集計を次のように組み合わせることができます:

1
2
buy_positions = [p for p in api.Positions if p.TradeType == TradeType.Buy]
avg_buy_price = sum([p.EntryPrice * p.VolumeInUnits for p in buy_positions]) / sum([p.VolumeInUnits for p in buy_positions])

同期および非同期操作

Pythonはデフォルトで同期実行モデルに従います。 アルゴリズムのライフサイクルメソッド(on_starton_tickon_bar_closedなど)は、プラットフォームのイベントループによって1つずつトリガーされます。

以下は同期取引フローの基本的な表現です:

graph TD
    A(シグナルを検出) ==> B(注文を実行)
    B ==> C(ポジションがTP/SLに到達)
    C ==> D(ポジションをクローズ)
    D ==> A

同期フローでは、長時間実行される操作がスレッドをブロックし、次のイベントを遅らせる可能性があります。 ただし、実際の戦略では、アルゴリズムは通常、注文を送信したり、ストップロスやテイクプロフィットを設定したりするなどの非ブロッキングアクションを開始し、その後、結果を処理するために新しいティックやバーなどの将来のイベントを待ちます。 この設定により、Pythonのasync/await構文を使用しなくても、非同期のようなオーケストレーションが実現されます。

注意

ほとんどの取引アルゴリズムでは、明示的な非同期プログラミングは不要です。

cTraderは、イベントベースのコールバックをシリアライズすることで並行性を管理します。 各メソッドは独立して呼び出され、並列で呼び出されることはないため、マルチスレッドのセーフガードは必要ありません。 この設計により、戦略はブロックされることなく、複数の取引条件に論理的に応答することができます。

以下の例では、戦略は買いと売りのシグナルを独立して評価し、実行コンテキストが重複することなく、それに応じて応答します。

graph TD
    A([シグナルに遭遇]) ==> B([買い注文を開く]) & C([ヘッジのために売り注文を開く]) ==> D([注文がテイクプロフィット<br>またはストップロスに到達])
    D ==> E([ポジションを閉じる])
    E ==> A

決定が並列に行われているように見えても、それらはcTraderの内部キューシステムによって順次処理されます。 プラットフォームのアーキテクチャは、並行性の問題を防ぎながら、取引ロボットが市場の変化に迅速に対応できるようにすることを目指しています。

Image title