質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.42%
Matplotlib

MatplotlibはPythonのおよび、NumPy用のグラフ描画ライブラリです。多くの場合、IPythonと連携して使われます。

NumPy

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

pandas

Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

Q&A

解決済

2回答

426閲覧

backtrader(バックテスト)に、注文を反映させたい。

kyaru

総合スコア1

Matplotlib

MatplotlibはPythonのおよび、NumPy用のグラフ描画ライブラリです。多くの場合、IPythonと連携して使われます。

NumPy

NumPyはPythonのプログラミング言語の科学的と数学的なコンピューティングに関する拡張モジュールです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

pandas

Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

0グッド

0クリップ

投稿2023/05/03 14:26

実現したいこと

ここに実現したいことを箇条書きで書いてください。

  • > backtrader(バックテスト)に、注文を反映させたい。

前提

エントリーロジックには、以下のように単純なものしか記載していないのですが、
バックテストを行うと、一切エントリーがされていない状態となっています。

self.buy() elif self.position: self.sell()
実行後: Final Portfolio Value: 100000.0 Sharpe Ratio: None Max Drawdown: 0.0 Annual Return: OrderedDict([(datetime.datetime(2020, 2, 26, 23, 25), 0.0)]) Sharpe Ratio Analysis: OrderedDict([('sharperatio', None)]) Drawdown Analysis: AutoOrderedDict([('len', 0), ('drawdown', 0.0), ('moneydown', 0.0), ('max', AutoOrderedDict([('len', 0.0), ('drawdown', 0.0), ('moneydown', 0.0)]))]) Annual Return Analysis: OrderedDict([(datetime.datetime(2020, 2, 26, 23, 25), 0.0)])

該当のソースコード

python

1 2class PandasDataFeed(btfeeds.PandasData): 3 4 params = ( 5 ('datetime', -1), 6 ('high', 'high'), 7 ('low', 'low'), 8 ('close', 'close'), 9 ) 10 11 # 既存のデータフレーム 'df' に対して、NaNを含む行を削除 12 df = df.dropna() 13 14 def __init__(self): 15 super(PandasDataFeed, self).__init__() 16 17class BollingerStrategy(btStrategy): 18 params = { 19 'time_period_to_close': 1, 20 } 21 22 def __init__(self): 23 self.first_bar = True 24 25 self.datetime = self.datas[0].datetime 26 self.high = self.datas[0].high 27 self.low = self.datas[0].low 28 self.close = self.datas[0].close 29 self.bar_executed = 0 30 31 def next(self): 32 33 if self.first_bar: 34 self.first_bar = False 35 return 36 37 self.buy() 38 39 if self.position:g 40 self.sell() 41 42 def log(self, txt, dt=None): 43 """ Logging function for this strategy""" 44 dt = dt or self.datas[0].datetime.datetime(0) 45 print('%s, %s' % (dt.isoformat(), txt)) 46 47class SafeAnnualReturn(btanalyzers.AnnualReturn): 48 def start(self): 49 super().start() 50 self._value_start = self.strategy.broker.getvalue() 51 52 def stop(self): 53 value_start = self._value_start 54 value_end = self.strategy.broker.getvalue() 55 56 if value_start != 0.0 and len(self.strategy.datetime.array) > 0: 57 annualret = (value_end / value_start) - 1.0 58 if len(self.rets) > 0: 59 self.rets[self.strategy.datetime.datetime(-1)] = annualret 60 else: 61 self.rets[self.strategy.datetime.datetime(-1)] = annualret 62 else: 63 if len(self.strategy.datetime.array) > 0: 64 self.rets[self.strategy.datetime.datetime(-1)] = 0.0 65 else: 66 self.rets[self.strategy.datetime.datetime(-1)] = 0.0 67 68 def get_analysis(self): 69 return self.rets 70 71# バックテストの実行と評価 72# Cerebroエンジンの初期化 73cerebro = bt.Cerebro() 74 75# データフィードの追加 76data_feed = PandasDataFeed(dataname=df) 77cerebro.adddata(data_feed) 78 79# 戦略の追加 80cerebro.addstrategy(BollingerStrategy) 81 82# 分析機能の追加 83cerebro.addanalyzer(btanalyzers.SharpeRatio, _name='sharpe_ratio') 84cerebro.addanalyzer(btanalyzers.DrawDown, _name='drawdown') 85cerebro.addanalyzer(SafeAnnualReturn, _name='annual_return') 86cerebro.addanalyzer(btanalyzers.SharpeRatio, _name='mySharpeRatio') 87 88# 初期資本の設定 89cerebro.broker.setcash(100000.0) 90cerebro.addsizer(bt.sizers.FixedSize, stake=1) 91 92# バックテストの実行 93results = cerebro.run() 94 95# 分析結果の取得 96sharpe_ratio = results[0].analyzers.sharpe_ratio.get_analysis()['sharperatio'] 97drawdown = results[0].analyzers.drawdown.get_analysis() 98annual_return = results[0].analyzers.annual_return.get_analysis() 99 100# 最終資産額と分析結果の表示 101print('Final Portfolio Value:', cerebro.broker.getvalue()) 102print('Sharpe Ratio:', sharpe_ratio) 103print('Max Drawdown:', drawdown['max']['drawdown']) 104print('Annual Return:', annual_return) 105 106#results変数を確認し、異常な値が含まれているかどうかをチェック 107print("Sharpe Ratio Analysis:", results[0].analyzers.sharpe_ratio.get_analysis()) 108print("Drawdown Analysis:", results[0].analyzers.drawdown.get_analysis()) 109print("Annual Return Analysis:", results[0].analyzers.annual_return.get_analysis()) 110 111# プロットの設定 112plt.style.use('seaborn-whitegrid') 113matplotlib.rcParams.update({'font.size': 8}) 114fig, ax1 = plt.subplots(figsize=(12, 6)) 115 116# バックテストの結果をプロット 117cerebro.plot(iplot=False, ax=ax1) 118 119# プロットの保存 120plt.savefig("backtest_results.png", dpi=300, bbox_inches='tight') 121 122# 結果をファイルに保存 123with open("backtest_results.txt", "w") as f: 124 f.write("Final Portfolio Value: {}\n".format(cerebro.broker.getvalue())) 125 f.write("Sharpe Ratio: {}\n".format(sharpe_ratio)) 126 f.write("Max Drawdown: {}\n".format(drawdown['max']['drawdown'])) 127 f.write("Annual Return: {}\n".format(annual_return)) 128 f.write("\n") 129 f.write("Sharpe Ratio Analysis: {}\n".format(results[0].analyzers.sharpe_ratio.get_analysis())) 130 f.write("Drawdown Analysis: {}\n".format(results[0].analyzers.drawdown.get_analysis())) 131 f.write("Annual Return Analysis: {}\n".format(results[0].analyzers.annual_return.get_analysis())) 132 133# バックテスト結果の詳細を表示 134print("Strategy analysis:") 135for k, v in results[0].analyzers.items(): 136 print(" {}: {}".format(k, v.get_analysis())) 137 138print("Backtest completed successfully.")

試したこと

データに欠損値がないことは確認し、欠損値があれば行ごと削除するという前処理は実行しています。
df上のデータ、計算後のデータもprintで確認し問題はありませんでした。

補足情報(FW/ツールのバージョンなど)

windows11

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2023/05/04 10:18

Backtraderは使ったことがないのですが、 どのあたりを見てみたら良いのかといった点で コメントしてみますね。 わからないですが、 注文を出しているけれども、 口座残高に変化がないのでしたら、 注文が約定されていないのかなと思いました。 *Backtraderの新規約定と決済約定の区別があるのかどうかもわからないですが・・ > 約定 (やくじょう) > 約定とは、株式取引などの売買が成立することをいいます。 > https://www.smbcnikko.co.jp/terms/japan/ya/J0059.html ストラテジーのnotify_orderメソッドで 約定の情報が取れるみたいですので、 このメソッドにログなどを埋め込んで確認してみたらいかがでしょうか。 それから、 バックテストしている銘柄と、 Backtraderで扱う単位の関係がわからないのですが、 注文を出して、それが成立するだけの取引数量になっていますでしょうか? 円単位?ドル単位?かわからないのですが、 口座残高が不足していたら約定しないのかな?と思いました。 もしQuickstartを試していないで、質問欄のコードを書き始めているのでしたら、 まずQuickstart Guideの次の内容を見て、実際に試してみる方が解決が早いかもしれませんね。 Adding some Logic to the Strategy Do not only buy … but SELL https://www.backtrader.com/docu/quickstart/quickstart/#adding-some-logic-to-the-strategy
guest

回答2

0

自己解決

皆さん、先日はMarginErrorについての質問に対して
たくさんのアドバイスをいただきありがとうございました。

その後、自己解決することができましたので、経過を共有させていただきます。

当初、成り行き注文でMarginErrorが発生していましたが、
指値注文に変更したところ、エラーが解消されました。
具体的には、以下のように価格を指定して注文を出すことで問題が解決しました。

if not self.position:
self.log('BUY CREATE, %.2f' % self.close[0])
buy_price = self.close[0]
self.order = self.buy(price=buy_price, exectype=bt.Order.Limit)
else:
self.log('SELL CREATE, %.2f' % self.close[0])
sell_price = self.close[0]
self.order = self.sell(price=sell_price, exectype=bt.Order.Limit)

原因はまだ完全には把握できていませんが、
価格を指定したことが何らかの形でMarginErrorを回避できたようです。

再度、皆さんのご支援に感謝いたします。今後ともよろしくお願いいたします。

投稿2023/05/10 13:13

kyaru

総合スコア1

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

注文されたことは確認できてますでしょうか?
恐らくbuyしたあとにsellしてるので損益が発生していないのかもしれません

投稿2023/05/04 00:09

a.com

総合スコア881

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kyaru

2023/05/04 01:05

ご連絡ありがとうございます。 以下のように対策を行いましたが状況が変わず困っています。 1.self.buy()の後にprint("market_buy")を追加しましたが、ログには出力されました。 2.ロングポジションを取った後にsellにならないように条件を付けました。 3.2を実行した際、self.log("Closing long position")が出力されませんでした。 4.終了時にポジションを解消するコード(def stop)を追加しました。 # ポジションがない場合、新しいポジションを作成 self.buy() print("market_buy") # ロングポジションがある場合 if self.position.size > 0: # 経過時間が指定された時間を超えた場合、ポジションを閉じる if (len(self) - self.bar_executed >= self.p.time_period_to_close): self.log("Closing long position") self.sell() def stop(self): # ポジションがある場合、ポジションを閉じる if self.position.size > 0: self.log("Closing long position on stop") self.sell() elif self.position.size < 0: self.log("Closing short position on stop") self.buy() 上記の1〜4を試しましたが、 バックテストの結果には注文した結果が反映されませんでした。(上記前提の、「実行後」のコードと変わらない結果が出ました。) 何か解決策があれば、教えていただけますでしょうか。 よろしくお願いいたします。
a.com

2023/05/04 10:58

約定するとnotify_tradeがコールされますのでそこでログを出力してみては?
kyaru

2023/05/04 12:07

早速のご連絡ありがとうございます。 以下のコードを追加したのですが、約定データが表示されませんでした。 def notify_order(self, order): if order.status in [order.Completed]: if order.isbuy(): self.log(f"BUY EXECUTED, Price: {order.executed.price:.2f}, Cost: {order.executed.value:.2f}, Comm: {order.executed.comm:.2f}") self.bar_executed = len(self) else: # Sell self.log(f"SELL EXECUTED, Price: {order.executed.price:.2f}, Cost: {order.executed.value:.2f}, Comm: {order.executed.comm:.2f}") def notify_trade(self, trade): if not trade.isclosed: return self.log(f"OPERATION PROFIT, GROSS: {trade.pnl:.2f}, NET: {trade.pnlcomm:.2f}") 確認するべき項目として、以下があると思いますが、困っています。 ・next() 関数内で買い注文(self.buy())や売り注文(self.sell())が実行されているか。  →self.buy()のみにしても約定されないので困っています。 ・データが正しく設定されているか。  →欠損値もなくしているので設定できているとは思いますが。。。
a.com

2023/05/04 13:41

約定されないということはnotify_orderでstatusがrejectなのでしょうか?
kyaru

2023/05/07 16:35

お世話になっております。 ご連絡遅くなり、申し訳ございません。 notify_orderについては、以下のように書き換えたのですが、 ログには、marginと出ました。 size=1、cashも10億で設定しているので、エラーが出るのはおかしいです。 【コード】 ① def notify_order(self, order): if order.status in [order.Submitted, order.Accepted]: return if order.status in [order.Completed]: if order.isbuy(): self.log(f"BUY EXECUTED, Price: {order.executed.price:.2f}, Cost: {order.executed.value:.2f}, Comm: {order.executed.comm:.2f}") self.bar_executed = len(self) else: # Sell self.log(f"SELL EXECUTED, Price: {order.executed.price:.2f}, Cost: {order.executed.value:.2f}, Comm: {order.executed.comm:.2f}") elif order.status in [order.Canceled, order.Margin, order.Rejected]: self.log(f"Order {order.Status[order.status]}") ② if self.position.size == 0: self.buy(size=1) self.log('Buy order sent') elif self.position.size > 0: self.sell(size=1) self.log('Sell order sent') ⓷ cerebro.broker.setcash(10000000000.0) 【ログ】 2020-02-26T23:24:00, Order Margin 2020-02-26T23:24:00, Next called 2020-02-26T23:24:00, Sell order sent 2020-02-26T23:25:00, Order Margin 補足: その他初期設定は以下のようにしています。 cerebro.broker.setcash(10000000000.0) cerebro.addsizer(bt.sizers.FixedSize, stake=1) cerebro.broker.setcommission(commission=0.001, margin=10.0) アドバイス、解決策いただけますと幸いです。
a.com

2023/05/07 17:52

statusがMarginなのでcashを超える大きな値がデータフィードに設定されているんじゃないでしょうか? notify_orderの最初でorderの中身をログに出力すれば良いのではないでしょうか
kyaru

2023/05/08 10:07 編集

ありがとうございます。 以下のようにorderの中身を出すように追記しました。 オーダーサイズが -1 であり、ポジションが 1 であるのにmarginなのが分かりません・・・ 追記内容: def notify_order(self, order): # 注文の状況を表示 print(f"Order: Ref: {order.ref}, Status: {order.getstatusname()}, Size: {order.size}, Price: {order.price}, Position: {self.position}") ログ: Order: Ref: 82045, Status: Margin, Size: -1, Price: None, Position: --- Position Begin - Size: 1 - Price: nan - Price orig: 0.0 - Closed: 0 - Opened: 1 - Adjbase: 4257739.0 --- Position End
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.42%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問