Function bodies 18 total
BacktestStrategy class · php · L8-L335 (328 LOC)app/Console/Commands/BacktestStrategy.php
class BacktestStrategy extends Command
{
protected $signature = 'trading:backtest
{threshold=0.5 : Breakout threshold percentage (e.g., 0.5 for 0.5%)}
{--lookback=20 : Lookback period for high/low}
{--stop-loss=1.0 : Stop loss percentage}
{--trailing-offset=0.3 : Trailing stop offset percentage}
{--max-positions=3 : Maximum positions per direction}
{--spread=0.1 : Maximum spread percentage}';
protected $description = 'Backtest trading strategy using historical price data';
private array $positions = [];
private array $closedTrades = [];
private int $nextPositionId = 1;
public function handle()
{
$threshold = (float) $this->argument('threshold');
$lookbackPeriod = (int) $this->option('lookback');
$stopLossPercent = (float) $this->option('stop-loss');
$trailingTradingExecute class · php · L14-L117 (104 LOC)app/Console/Commands/TradingExecute.php
class TradingExecute extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'trading:execute';
/**
* The console command description.
*
* @var string
*/
protected $description = '自動トレーディングを実行';
/**
* Execute the console command.
*/
public function handle()
{
$this->info('トレーディング実行開始: ' . now());
try {
// 有効な設定を取得
$settings = TradingSettings::where('is_active', true)->get();
if ($settings->isEmpty()) {
$this->warn('有効なトレーディング設定がありません');
return 0;
}
// Exchange クライアントを選択 (Paper or Live)
$exchangeClient = $this->getExchangeClient();
foreach ($settings as $setting) {
$this->info("処理中: {$setting->name} ({$setting->symbol})");
try {
// ストラテジーインスタンスを作成
Controller class · php · L5-L8 (4 LOC)app/Http/Controllers/Controller.php
abstract class Controller
{
//
}TradingNotification class · php · L12-L87 (76 LOC)app/Mail/TradingNotification.php
class TradingNotification extends Mailable
{
use Queueable, SerializesModels;
public string $action; // 'entry' or 'exit'
public string $side; // 'long' or 'short'
public string $symbol;
public float $price;
public float $quantity;
public ?float $profitLoss;
public ?float $profitLossPercent;
public ?string $reason;
public ?string $strategyName;
public array $additionalData;
/**
* Create a new message instance.
*/
public function __construct(
string $action,
string $side,
string $symbol,
float $price,
float $quantity,
?float $profitLoss = null,
?float $profitLossPercent = null,
?string $reason = null,
?string $strategyName = null,
array $additionalData = []
) {
$this->action = $action;
$this->side = $side;
$this->symbol = $symbol;
$this->price = $price;
$this->quantity = $quantity;
$this->profiPosition class · php · L7-L55 (49 LOC)app/Models/Position.php
class Position extends Model
{
protected $fillable = [
'symbol',
'trading_settings_id',
'side',
'quantity',
'entry_price',
'entry_fee',
'trailing_stop_price',
'exit_order_id',
'exit_order_price',
'exit_price',
'exit_fee',
'status',
'profit_loss',
'opened_at',
'closed_at',
];
protected $casts = [
'quantity' => 'decimal:8',
'entry_price' => 'decimal:8',
'entry_fee' => 'decimal:8',
'trailing_stop_price' => 'decimal:8',
'exit_order_price' => 'decimal:8',
'exit_price' => 'decimal:8',
'exit_fee' => 'decimal:8',
'profit_loss' => 'decimal:8',
'opened_at' => 'datetime',
'closed_at' => 'datetime',
];
/**
* 合計手数料を取得
*/
public function getTotalFeeAttribute(): float
{
return (float)($this->entry_fee ?? 0) + (float)($this->exit_fee ?? 0);
}
/**
PriceHistory class · php · L7-L21 (15 LOC)app/Models/PriceHistory.php
class PriceHistory extends Model
{
protected $table = 'price_history';
protected $fillable = [
'symbol',
'price',
'recorded_at',
];
protected $casts = [
'price' => 'decimal:8',
'recorded_at' => 'datetime',
];
}TradingLog class · php · L7-L24 (18 LOC)app/Models/TradingLog.php
class TradingLog extends Model
{
protected $fillable = [
'symbol',
'action',
'quantity',
'price',
'result',
'message',
'executed_at',
];
protected $casts = [
'quantity' => 'decimal:8',
'price' => 'decimal:8',
'executed_at' => 'datetime',
];
}Repobility · code-quality intelligence platform · https://repobility.com
TradingSettings class · php · L7-L21 (15 LOC)app/Models/TradingSettings.php
class TradingSettings extends Model
{
protected $fillable = [
'name',
'symbol',
'strategy',
'parameters',
'is_active',
];
protected $casts = [
'parameters' => 'array',
'is_active' => 'boolean',
];
}User class · php · L10-L48 (39 LOC)app/Models/User.php
class User extends Authenticatable
{
/** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var list<string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var list<string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
}AppServiceProvider class · php · L7-L24 (18 LOC)app/Providers/AppServiceProvider.php
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
//
}
}PaperTradingClient class · php · L11-L359 (349 LOC)app/Trading/Exchange/PaperTradingClient.php
class PaperTradingClient implements ExchangeClient
{
private array $balance;
private array $positions = [];
private array $limitOrders = [];
private GMOCoinClient $realDataClient;
public function __construct()
{
// 初期残高を設定(USDTで10,000ドル)
$this->balance = Cache::get('paper_balance', [
'USDT' => 10000.0,
]);
$this->positions = Cache::get('paper_positions', []);
$this->limitOrders = Cache::get('paper_limit_orders', []);
// 実際の価格データ取得用のクライアント
$this->realDataClient = new GMOCoinClient();
}
public function getMarketData(string $symbol, int $limit = 100): array
{
// GMOCoinから実際の価格データを取得
return $this->realDataClient->getMarketData($symbol, $limit);
}
public function buy(string $symbol, float $quantity, ?float $price = null): array
{
$executionPrice = $price ?? $this->getCurrentPrice($symbol);
$cost = $quantity * $executionPrice;
// 手数HighLowBreakoutStrategy class · php · L12-L162 (151 LOC)app/Trading/Strategy/HighLowBreakoutStrategy.php
class HighLowBreakoutStrategy extends TradingStrategy
{
/**
* 市場データを分析してトレーディングシグナルを生成
*
* @param array $marketData 市場データ(prices配列を含む)
* @return array ['action' => 'buy'|'sell'|'hold', 'quantity' => float, 'price' => float|null]
*/
public function analyze(array $marketData): array
{
$params = $this->getParameters();
$lookbackPeriod = $params['lookback_period'] ?? 20;
$breakoutThreshold = $params['breakout_threshold'] ?? 0.4;
$trendFilterEnabled = $params['trend_filter_enabled'] ?? true;
$trendMaPeriod = $params['trend_ma_period'] ?? 60;
$trendThreshold = $params['trend_threshold'] ?? 0.3;
$prices = $marketData['prices'];
$symbol = $marketData['symbol'];
// 価格データが不足している場合はホールド
if (count($prices) < $lookbackPeriod + 1) {
Log::info('Insufficient data for High-Low Breakout', [
'symbol' => $symbol,
'available' => count($prices),
RSIContrarianStrategy class · php · L25-L367 (343 LOC)app/Trading/Strategy/RSIContrarianStrategy.php
class RSIContrarianStrategy extends TradingStrategy
{
private ?float $currentRSI = null;
/**
* クールダウン中かどうかをチェック
*
* RSI逆張り戦略で負けトレードがあった場合、指定時間内はエントリーをスキップ
* 全通貨ペアに適用(仮想通貨は相関が高いため)
*
* @param int $cooldownMinutes クールダウン時間(分)
* @return bool クールダウン中の場合true
*/
private function isInCooldown(int $cooldownMinutes): bool
{
if ($cooldownMinutes <= 0) {
return false;
}
$cooldownThreshold = now()->subMinutes($cooldownMinutes);
// RSI逆張り戦略の全設定IDを取得
$rsiStrategyIds = TradingSettings::where('strategy', 'like', '%RSIContrarianStrategy%')
->pluck('id')
->toArray();
if (empty($rsiStrategyIds)) {
return false;
}
// 指定時間内に負けトレード(profit_loss < 0)があるかチェック
// 通貨ペアによらず、RSI逆張り戦略全体でチェック
$recentLoss = Position::where('status', 'closed')
->whereIn('trading_settings_id', $rsiStrategyIds)
->where('profit_SimpleMovingAverageStrategy class · php · L9-L82 (74 LOC)app/Trading/Strategy/SimpleMovingAverageStrategy.php
class SimpleMovingAverageStrategy extends TradingStrategy
{
public function analyze(array $marketData): array
{
$params = $this->getParameters();
$shortPeriod = $params['short_period'] ?? 5;
$longPeriod = $params['long_period'] ?? 20;
// 価格データから移動平均を計算
$prices = $marketData['prices'] ?? [];
if (count($prices) < $longPeriod) {
return ['action' => 'hold', 'quantity' => 0, 'price' => 0];
}
$shortMA = $this->calculateMA($prices, $shortPeriod);
$longMA = $this->calculateMA($prices, $longPeriod);
$currentPrice = end($prices);
$tradeSize = $params['trade_size'] ?? 0.01;
// ゴールデンクロス
if ($shortMA > $longMA && $this->previousCross($prices, $shortPeriod, $longPeriod) === 'golden') {
return [
'action' => 'buy',
'quantity' => $tradeSize,
'price' => $currentPrice
];
}
// デッドクロス
TradingStrategy class · php · L10-L86 (77 LOC)app/Trading/Strategy/TradingStrategy.php
abstract class TradingStrategy
{
protected TradingSettings $settings;
public function __construct(TradingSettings $settings)
{
$this->settings = $settings;
}
/**
* 市場データを分析して取引シグナルを生成
*
* @param array $marketData 市場データ(価格、出来高など)
* @return array ['action' => 'buy'|'sell'|'hold', 'quantity' => float, 'price' => float]
*/
abstract public function analyze(array $marketData): array;
/**
* ストラテジーのパラメータを取得
*/
public function getParameters(): array
{
return $this->settings->parameters ?? [];
}
/**
* 戦略名を取得
*/
public function getName(): string
{
return $this->settings->name ?? class_basename(static::class);
}
/**
* 戦略設定IDを取得
*/
public function getSettingsId(): int
{
return $this->settings->id;
}
/**
* トレンド方向を判定
*
* 移動平均線からの乖離率でトレンドを判定する
* - 現在価格がMAより閾値以上高い → 上昇トレンド
* - 現在価格がMAより閾値以上低い → 下落トレンド
* - Open data scored by Repobility · https://repobility.com
UserFactory class · php · L12-L44 (33 LOC)database/factories/UserFactory.php
class UserFactory extends Factory
{
/**
* The current password being used by the factory.
*/
protected static ?string $password;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),
'remember_token' => Str::random(10),
];
}
/**
* Indicate that the model's email address should be unverified.
*/
public function unverified(): static
{
return $this->state(fn (array $attributes) => [
'email_verified_at' => null,
]);
}
}DatabaseSeeder class · php · L9-L25 (17 LOC)database/seeders/DatabaseSeeder.php
class DatabaseSeeder extends Seeder
{
use WithoutModelEvents;
/**
* Seed the application's database.
*/
public function run(): void
{
// User::factory(10)->create();
User::factory()->create([
'name' => 'Test User',
'email' => '[email protected]',
]);
}
}TradingSettingsSeeder class · php · L8-L52 (45 LOC)database/seeders/TradingSettingsSeeder.php
class TradingSettingsSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// GMOコイン用のサンプル設定
TradingSettings::create([
'name' => 'BTC移動平均戦略(GMOコイン)',
'symbol' => 'BTC/JPY',
'strategy' => 'App\\Trading\\Strategy\\SimpleMovingAverageStrategy',
'parameters' => [
'short_period' => 5,
'long_period' => 20,
'trade_size' => 0.01,
],
'is_active' => false, // デフォルトは無効にしておく
]);
TradingSettings::create([
'name' => 'ETH移動平均戦略(GMOコイン)',
'symbol' => 'ETH/JPY',
'strategy' => 'App\\Trading\\Strategy\\SimpleMovingAverageStrategy',
'parameters' => [
'short_period' => 10,
'long_period' => 30,
'trade_size' => 0.1,
],
'is_active' => false,
]);
TradingSettings::create([