Aracı Kararlarını Geriye Dönük Test Etme
Performansı doğrulamak için aracı stratejilerini geçmiş veriler üzerinde benzetimle sınama.
Aracı Kararlarını Geriye Dönük Test Etme, CoddyKit'te ücretsiz bir AI Agents dersidir. Bu, 4 dersinin 4. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, AI Agents öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. AI Agents kursu toplamda 4 dersten oluşur.
Aracı Kararlarını Neden Geriye Dönük Sınamalısınız?
Finansal aracınızın iyi öneriler üretip üretmediğini geçmiş verilerle sınamadan bilemezsiniz. Geriye dönük sınama, geçmiş piyasa koşullarını aracının karar mantığından geçirir ve bu kararların nasıl sonuçlanacağını ölçer.
Bu, strateji kalitesini canlıya almadan önce doğrulamak için kullanılan temel araçtır.
Geleceğe Bakma Yanlılığı Tuzağı
Geleceğe bakma yanlılığı, geriye dönük sınamadaki en yaygın ve en tehlikeli hatadır: “geçmişteki” bir kararı verirken gelecekteki verileri kullanmak. Örneğin, aynı günün açılışında alım yapıp yapmamaya karar vermek için günün kapanış fiyatını kullanmak.
Geçmişteki her karar yalnızca benzetilen karar anında mevcut olan verileri kullanmalıdır.
import pandas as pd
import yfinance as yf
# WRONG — uses today's close to decide today's trade
def bad_signal(history: pd.DataFrame, date: str) -> str:
today_close = history.loc[date, 'Close'] # look-ahead!
if today_close > history['Close'].mean():
return 'BUY'
return 'HOLD'
# CORRECT — uses only data available BEFORE the decision date
def good_signal(history: pd.DataFrame, date: str) -> str:
past_data = history.loc[:date].iloc[:-1] # exclude today
if past_data.empty:
return 'HOLD'
today_open = history.loc[date, 'Open'] # open price known at market open
if today_open > past_data['Close'].mean():
return 'BUY'
return 'HOLD'Geçmişi Yeniden Oynatma Döngüsünü Oluşturma
Geçmişteki her tarih boyunca ilerleyin. Her adımda aracı yalnızca o ana kadar mevcut olan verileri görür. Kararı kaydedin, ardından sonucu gözlemlemek için sonraki tarihe geçin.
import yfinance as yf
import pandas as pd
def backtest_simple(ticker: str, start: str, end: str) -> list[dict]:
hist = yf.Ticker(ticker).history(start=start, end=end)
results = []
for i in range(20, len(hist)): # need 20 days of history for signals
window = hist.iloc[:i] # data available on day i
today = hist.iloc[i]
decision = good_signal(window, str(hist.index[i].date()))
results.append({
'date': str(hist.index[i].date()),
'decision': decision,
'open': float(today['Open']),
'close': float(today['Close']),
'next_day_return': None # filled in next iteration
})
return resultsHer Adımda Aracı Mantığını Benzetme
Daha gerçekçi bir geriye dönük sınama için her tarihte aracının gerçekleştirdiği tam araç çağrısını benzetin — istemin oluşturulması, LLM çağrısı ve kararın ayrıştırılması dâhil. Bu, yalnızca sinyal mantığını değil, sistemin tamamını sınar.
import openai, json
import yfinance as yf
client = openai.OpenAI(api_key='YOUR_OPENAI_KEY')
def agent_decision_at_date(ticker: str, hist_up_to: pd.DataFrame) -> str:
recent = hist_up_to.tail(20)
closes = recent['Close'].tolist()
returns = [round((closes[i] - closes[i-1]) / closes[i-1], 4)
for i in range(1, len(closes))]
prompt = (
f'Ticker: {ticker}\n'
f'Last 20 closing prices: {closes}\n'
f'Daily returns: {returns}\n'
f'Based on this data, recommend: BUY, SELL, or HOLD.\n'
f'Return JSON: {{"action": "BUY/SELL/HOLD", "reason": "..."}}'
)
resp = client.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': prompt}],
response_format={'type': 'json_object'}
)
return json.loads(resp.choices[0].message.content).get('action', 'HOLD')İsabet Oranını Hesaplama
İsabet oranı, BUY kararlarının yüzde kaçının pozitif bir ertesi gün getirisiyle takip edildiğini ölçer. %50'nin üzerindeki bir isabet oranı, aracının yönü rastlantıdan daha sık doğru tahmin ettiği anlamına gelir.
def compute_hit_rate(results: list[dict]) -> dict:
buy_decisions = [r for r in results if r['decision'] == 'BUY'
and r.get('next_day_return') is not None]
if not buy_decisions:
return {'hit_rate': None, 'sample_size': 0}
correct = sum(1 for r in buy_decisions if r['next_day_return'] > 0)
return {
'hit_rate': round(correct / len(buy_decisions), 4),
'sample_size': len(buy_decisions),
'correct': correct,
'interpretation': 'above_chance' if correct / len(buy_decisions) > 0.52 else 'at_or_below_chance'
}
if __name__ == '__main__':
results = [
{'decision': 'BUY', 'next_day_return': 0.012},
{'decision': 'BUY', 'next_day_return': -0.004},
{'decision': 'HOLD', 'next_day_return': None},
{'decision': 'BUY', 'next_day_return': 0.007},
]
print('Hit rate:', compute_hit_rate(results))
Referansa Göre Getiriyi Hesaplama
Strateji getirisini al ve tut referansıyla karşılaştırın. Aracı yalnızca endeksi elde tutmaktan daha düşük performans gösteriyorsa karmaşıklığı haklı çıkarılamaz.
def compare_to_benchmark(results: list[dict], ticker: str, period: str = '1y') -> dict:
import yfinance as yf
# Strategy return: only invested on BUY days
buy_days = [r for r in results if r['decision'] == 'BUY'
and r.get('next_day_return') is not None]
strategy_return = sum(r['next_day_return'] for r in buy_days)
# Benchmark: buy-and-hold
hist = yf.Ticker(ticker).history(period=period)['Close']
bh_return = float((hist.iloc[-1] - hist.iloc[0]) / hist.iloc[0])
return {
'strategy_return_pct': round(strategy_return * 100, 2),
'benchmark_return_pct': round(bh_return * 100, 2),
'outperformed': strategy_return > bh_return,
'alpha_pct': round((strategy_return - bh_return) * 100, 2)
}İşlem Maliyetlerinin Etkisi
İşlem maliyetleri (komisyonlar, alış-satış farkı, kayma) hesaba katılmadığında bir strateji kârlı görünebilir, ancak gerçekte uygulamada para kaybedebilir. İşlem başına yalnızca %0,1'lik bir maliyet bile çok sayıda işlem boyunca bileşik olarak önemli ölçüde artar.
COMMISSION_PCT = 0.001 # 0.1% per trade (buy + sell)
SLIPPAGE_PCT = 0.0005 # 0.05% per trade
def returns_after_costs(decisions: list[dict]) -> dict:
total_return = 0.0
total_cost = 0.0
for d in decisions:
if d['decision'] == 'BUY' and d.get('next_day_return') is not None:
gross = d['next_day_return']
cost = COMMISSION_PCT + SLIPPAGE_PCT
net = gross - cost
total_return += net
total_cost += cost
return {
'gross_return_pct': round((total_return + total_cost) * 100, 2),
'net_return_pct': round(total_return * 100, 2),
'total_costs_pct': round(total_cost * 100, 2),
'num_trades': sum(1 for d in decisions if d['decision'] == 'BUY')
}
if __name__ == '__main__':
decisions = [
{'decision': 'BUY', 'next_day_return': 0.012},
{'decision': 'BUY', 'next_day_return': -0.004},
{'decision': 'HOLD', 'next_day_return': None},
]
print('Returns after trading costs:', returns_after_costs(decisions))
İleriye Dönük Doğrulama
Örneklem içi optimizasyon (sınadığınız dönemle aynı dönem üzerinde eğitim yapmak) aşırı uyuma yol açar. İleriye dönük doğrulama, gerçek uygulamadaki gibi, kayan bir eğitim penceresinin ardından yeni bir sınama dönemi kullanır.
def walk_forward_test(ticker: str, total_years: int = 3,
train_months: int = 12, test_months: int = 3) -> list[dict]:
import yfinance as yf
from dateutil.relativedelta import relativedelta
from datetime import date
results = []
start = date(date.today().year - total_years, 1, 1)
end = date.today()
window_start = start
while True:
train_end = window_start + relativedelta(months=train_months)
test_end = train_end + relativedelta(months=test_months)
if test_end > end:
break
# In practice: train your signal on window_start->train_end
# then evaluate on train_end->test_end
results.append({
'train_period': f'{window_start} to {train_end}',
'test_period': f'{train_end} to {test_end}'
})
window_start += relativedelta(months=test_months)
return resultsGeriye Dönük Sınama Raporu
Geriye dönük sınama sonuçlarını yapılandırılmış bir raporda özetleyin: isabet oranı, referansa göre toplam getiri, alfa, Sharpe oranı, azami düşüş, işlem sayısı ve maliyetler sonrası getiri.
def backtest_report(ticker: str, results: list[dict]) -> dict:
hit = compute_hit_rate(results)
benchmark = compare_to_benchmark(results, ticker)
costs = returns_after_costs(results)
return {
'ticker': ticker,
'total_decisions': len(results),
'buy_signals': costs['num_trades'],
'hit_rate': hit.get('hit_rate'),
'strategy_return_gross': f'{costs["gross_return_pct"]}%',
'strategy_return_net': f'{costs["net_return_pct"]}%',
'benchmark_return': f'{benchmark["benchmark_return_pct"]}%',
'alpha': f'{benchmark["alpha_pct"]}%',
'outperformed_benchmark': benchmark['outperformed'],
'transaction_costs': f'{costs["total_costs_pct"]}%'
}Geriye Dönük Sınama Sonuçlarını Yorumlama
İyi geriye dönük sınama sonuçları gelecekteki performansı garanti etmez; ancak kötü sonuçlar, uygulanmaması gereken stratejileri belirler. Temel uyarı işaretleri şunlardır: %50'nin altında isabet oranı, maliyetlerden sonra negatif alfa, 0,5'in altında Sharpe oranı veya risk toleransınızı aşan azami düşüş.
def interpret_backtest(report: dict) -> list[str]:
warnings = []
hit_rate = report.get('hit_rate')
if hit_rate and hit_rate < 0.50:
warnings.append(f'Hit rate {hit_rate:.1%} is below chance — strategy predicts direction poorly')
alpha_str = report.get('alpha', '0%')
alpha = float(alpha_str.replace('%', '')) / 100
if alpha < 0:
warnings.append(f'Negative alpha {alpha_str}: strategy underperforms buy-and-hold')
if not warnings:
return ['Backtest results look acceptable — proceed with paper trading before live deployment']
return warnings
if __name__ == '__main__':
report = {'hit_rate': 0.45, 'alpha': '-1.2%'}
for warning in interpret_backtest(report):
print('-', warning)
Sağlamlık İçin Monte Carlo Benzetimi
Tek bir geriye dönük sınama, seçilen belirli zaman aralığına duyarlıdır. Monte Carlo benzetimi, performansın farklı diziler boyunca geçerli olup olmadığını sınamak için geçmiş getirilerin sırasını rastgele değiştirir ve stratejinin sağlam mı yoksa yalnızca şanslı mı olduğunu ortaya çıkarır.
import numpy as np
def monte_carlo_backtest(daily_returns: list[float],
simulations: int = 1000) -> dict:
returns_arr = np.array(daily_returns)
final_returns = []
for _ in range(simulations):
shuffled = np.random.choice(returns_arr, size=len(returns_arr), replace=True)
cumulative = float((1 + shuffled).prod() - 1)
final_returns.append(cumulative)
final_returns = sorted(final_returns)
return {
'median_return': round(float(np.median(final_returns)) * 100, 2),
'percentile_5': round(float(np.percentile(final_returns, 5)) * 100, 2),
'percentile_95': round(float(np.percentile(final_returns, 95)) * 100, 2),
'prob_positive': round(sum(1 for r in final_returns if r > 0) / simulations, 3)
}Geriye Dönük Sınamada Geleceğe Bakma Yanlılığı Nedir?
Geleceğe bakma yanlılığı, geriye dönük sınama performansını yanlışlıkla olduğundan yüksek göstermenin en yaygın yoludur. Kesin tanımını anlamak, hatalı geriye dönük sınamalar oluşturmanızı önler.
Aracı Kararlarının Geriye Dönük Sınaması Özeti
Sağlam bir geriye dönük sınama şunları gerektirir: geleceğe bakma yanlılığından kaçınmak (kararlar yalnızca geçmiş verileri kullanır), her geçmiş adımda gerçekçi aracı mantığını benzetmek, isabet oranını ve referansa göre alfanın ölçülmesi, işlem maliyetlerinin hesaba katılması ve aşırı uyumu tespit etmek için ileriye dönük bölmelerle doğrulama.
Sıkça Sorulan Sorular
“Aracı Kararlarını Geriye Dönük Test Etme” dersi ücretsiz mi?
Evet — “Aracı Kararlarını Geriye Dönük Test Etme” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve AI Agents kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. AI Agents kursu toplamda 4 dersten oluşur.
“Aracı Kararlarını Geriye Dönük Test Etme” dersinde ne öğreneceğim?
Performansı doğrulamak için aracı stratejilerini geçmiş veriler üzerinde benzetimle sınama. AI Agents ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.
AI Agents öğrenmeye başlamak için deneyim gerekli mi?
Önceden deneyim gerekmez. CoddyKit'te AI Agents, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 4. dersidir.
“Aracı Kararlarını Geriye Dönük Test Etme” dersi ne kadar sürer?
Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.
Bu AI Agents dersinde kod yazıp çalıştırabilir miyim?
Evet. Her AI Agents dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.
Bu kursun tüm dersleri
- Piyasa Verisi API Entegrasyonu
- Portföy Analizi Aracı Araçları
- Risk ve Uyumluluk Güvenlik Sınırları
- Aracı Kararlarını Geriye Dönük Test Etme