format("Y-m-d H:i:s") . "
";
echo basename(__FILE__) . "
";
// 創建 OKX 的實例
$exchange = new ccxt\okx(array(
'enableRateLimit' => true, // 開啟速率限制
));
// 呼叫函式並獲取 coinList
$coinList = getOkxPairs();
//----------基本設定----------
//----------基本設定----------
// 觀察清單
// $coinList = [
// 'BTC','ETH','SOL','TON','DOGE','XRP','PNUT','PEPE','UXLINK','X','ACT','1INCH','AAVE','ACE','ACH','ADA','AEVO','AGLD','AIDOGE','ALGO','ALPHA','APE','API3','APT','AR','ARB','ATH','ATOM','AUCTION','AVAX','AXS','BADGER','BAL','BAND','BAT','BCH','BICO','BIGTIME','BLUR','BNB','BNT','BOME','BONK','BONE','BRETT','BSV','CAT','CATI','CELO','CETUS','CFX','CHZ','COMP','CORE','CRO','CRV','CSPR','CTC','CVC','CVX','DGB','DOGS','DOT','DYDX','EGLD','EIGEN','ENJ','ENS','EOS','ETC','ETHW','ETHFI','FIL','FLM','FLOKI','FLOW','FOXY','FTM','FXS','GALA','GAS','GFT','GLM','GMT','GMX','GOAT','GODS','GRASS','GRT','HBAR','HMSTR','ICP','ICX','ID','IMX','INJ','IOST','IOTA','JOE','JST','JTO','JUP','KISHU','KNC','KSM','LDO','LINK','LOOKS','LPT','LQTY','LRC','LSK','LTC','LUNA','LUNC','MAGIC','MANA','MASK','MAX','MEME','MERL','METIS','MEW','MINA','MKR','MOODENG','MOVR','NEAR','NEIROETH','NEIRO','NEO','NFT','NMR','NOT','OM','ONDO','ONE','ONT','OP','ORBS','ORDI','PEOPLE','PERP','POL','POPCAT','PRCL','PUFFER','PYTH','QTUM','RACA','RAY','RDNT','RENDER','RON','RSR','RVN','SAND','SATS','SCR','SHIB','SLP','SNX','SSV','STORJ','STRK','STX','SUI','SUSHI','SWEAT','T','TAO','THETA','TIA','TNSR','TRB','TRX','TURBO','ULTI','UMA','UNI','USTC','VELO','VRA','W','WAXP','WIF','WLD','WOO','XCH','XLM','XTZ','YFI','YGG','ZENT','ZETA','ZIL','ZK','ZRO','ZRX'
// ];
// $coinList = [
// 'BTC',
// 'GGG',
// 'SOL',
// 'TON',
// 'DOGE'
// ];
$totalEq = totalEq();
$coinCount = count($coinList);
// $amountInUSDT = ($totalEq) / $coinCount; // 下單金額
$amountInUSDT = 20; // 下單金額
//$amountInUSDT = 5; // 下單金額
echo "totalEq:$totalEq
";
echo "coinCount:$coinCount
";
echo "下單金額:$amountInUSDT
";
//----------第一部分----------
//----------第一部分----------
$markets = load_markets(); // 加載市場,查詢合約面值ctVal
$fetch_positions = fetch_positions(); // 加載持倉
// 依序顯示觀察清單中的交易對
foreach ($coinList as $coin) {
$symbol = $coin . '/USDT:USDT';
echo "
查看交易對: " . $symbol . "
";
$timeframe = '1d'; // K 線時間周期,1d 表示日線
$limit = 100; // 設定最多獲取最近100筆資料
try {
// 獲取最近100筆 K 線資料
$ohlcv = $exchange->fetch_ohlcv($symbol, $timeframe, null, $limit);
// 檢查資料是否正確返回
if (empty($ohlcv)) {
throw new Exception("無法從 OKX 獲取資料,請檢查 API 配置或查詢參數");
}
// 儲存所有資料及訊號資訊
$comparison_results = []; // 儲存比較結果
$processed_data = []; // 儲存處理後的 K 線資料
$latest_signal_data = null; // 儲存最新的訊號資料
$second_latest_signal_data = null; // 儲存倒數第二筆訊號資料
// 處理每一筆 K 線資料
for ($i = 0; $i < count($ohlcv); $i++) {
// 當前日期與收盤價
$date = date('Y-m-d', $ohlcv[$i][0] / 1000); // 轉換為日期格式(將 Unix 時間戳轉為日期)
$c0_close = $ohlcv[$i][4]; // 當前收盤價 (C[0])
// 31 天前的收盤價
$c31_close = "N/A"; // 預設為 "N/A"
if ($i - 31 >= 0) {
$c31_close = $ohlcv[$i - 31][4]; // 取得 31 天前的收盤價
}
// 比較 C[0] 與 C[31]
$comparison_result = "N/A"; // 預設比較結果為 "N/A"
if ($c31_close !== "N/A") {
// $comparison_result = ($c0_close > $c31_close) ? "1" : "0"; // 如果 C[0] 大於 C[31],設定為 "1",否則為 "0"
if ($c0_close >= $c31_close * (1 + 0.02)) {
$comparison_result = "1"; // 當 C[0] >= C[31] 的 102% 時
} elseif ($c0_close < $c31_close * (1 - 0.02)) {
$comparison_result = "-1"; // 當 C[0] < C[31] 的 98% 時
} else {
$comparison_result = "0";
}
}
// 記錄比較結果
$comparison_results[] = $comparison_result;
// 上一筆比較結果
$previous_comparison_result = $i > 0 ? $comparison_results[$i - 1] : "N/A"; // 獲取上一筆的比較結果
// 訊號邏輯
// $signal = "N/A"; // 預設為 "N/A"
// if ($previous_comparison_result === "0" && $comparison_result === "1") {
// $signal = "LONG"; // 若上一筆為 "0" 且當前為 "1",設定為 "LONG"
// } elseif ($previous_comparison_result === "1" && $comparison_result === "0") {
// $signal = "SHORT"; // 若上一筆為 "1" 且當前為 "0",設定為 "SHORT"
// }
// 訊號邏輯
$signal = "N/A"; // 預設為 "N/A"
if (($previous_comparison_result === "0" || $previous_comparison_result === "-1") && $comparison_result === "1") {
$signal = "LONG"; // 從 0 或 -1 變 1,顯示 LONG
} elseif (($previous_comparison_result === "0" || $previous_comparison_result === "1") && $comparison_result === "-1") {
$signal = "SHORT"; // 從 0 或 1 變 -1,顯示 SHORT
}
// 儲存處理後的資料
$processed_data[] = [
'date' => $date, // 日期
'c0_close' => $c0_close, // 當前收盤價
'c31_close' => $c31_close, // 31 天前的收盤價
'previous_comparison_result' => $previous_comparison_result, // 上一筆比較結果
'comparison_result' => $comparison_result, // 當前比較結果
'signal' => $signal // 訊號 ("LONG" 或 "SHORT")
];
// 更新最新與倒數第二筆訊號
if ($signal === "LONG" || $signal === "SHORT") {
$second_latest_signal_data = $latest_signal_data; // 更新倒數第二筆訊號資料
$latest_signal_data = end($processed_data); // 更新最新的訊號資料
}
}
// 顯示昨天與今天的完整資料
echo "昨天與今天的資料:
";
echo "";
echo "| 日期 | 收盤價 (C[0]) | 31 天前的收盤價 (C[31]) | 上一筆比較結果 | 比較結果 | 訊號 |
";
// 取出最後兩筆資料
$last_two_data = array_slice($processed_data, -2);
foreach ($last_two_data as $data) {
echo "";
echo "| " . $data['date'] . " | ";
echo "" . $data['c0_close'] . " | ";
echo "" . $data['c31_close'] . " | ";
echo "" . $data['previous_comparison_result'] . " | ";
echo "" . $data['comparison_result'] . " | ";
echo "" . $data['signal'] . " | ";
echo "
";
}
echo "
";
// 顯示最新與倒數第二筆帶有訊號的資料
echo "最後兩筆訊號資料:
";
echo "";
echo "| 日期 | 收盤價 (C[0]) | 31 天前的收盤價 (C[31]) | 上一筆比較結果 | 比較結果 | 訊號 |
";
// 顯示最後兩筆帶有訊號的資料
foreach ([$second_latest_signal_data, $latest_signal_data] as $signal_data) {
if ($signal_data !== null) {
echo "";
echo "| " . $signal_data['date'] . " | ";
echo "" . $signal_data['c0_close'] . " | ";
echo "" . $signal_data['c31_close'] . " | ";
echo "" . $signal_data['previous_comparison_result'] . " | ";
echo "" . $signal_data['comparison_result'] . " | ";
echo "" . $signal_data['signal'] . " | ";
echo "
";
}
}
echo "
";
// 取得昨天的日期
$yesterday_date = date('Y-m-d', strtotime('-1 day')); // 昨日日期
// 遍歷 processed_data 並找出 date 等於昨天的資料
foreach ($processed_data as $data) {
if ($data['date'] === $yesterday_date) { // 如果日期等於昨天
// 設置下單訊號變數為 $data['signal']
$order_signal = $data['signal'];
// 印出昨天日期的資料
echo "昨天的資料:\n";
echo "日期: " . $data['date'] . "\n"; // 日期
echo "收盤價 (C[0]): " . $data['c0_close'] . "\n"; // 當前收盤價
echo "訊號: " . $data['signal'] . "
"; // 訊號 ("LONG" 或 "SHORT")
// 顯示下單訊號
echo "下單訊號: " . $order_signal . "
"; // 顯示下單訊號
}
}
$instId = $coin . '-USDT-SWAP';
$position = searchposition($instId);
//定義marketposition、position
if (isset($position)) {
if ($position > 0) {
$marketposition = 1;
} elseif ($position < 0) {
$marketposition = -1;
} else {
$position = 0;
$marketposition = 0;
}
} else {
$position = 0;
$marketposition = 0;
}
echo "$instId 的 pos: " . $position . "
";
echo "最新報價: " . $c0_close . "
";
$searchmarkets = searchmarkets($instId);
$ctVal = $searchmarkets['ctVal']; // 合約面值ctVal
$minSz = $searchmarkets['minSz']; //最小下單數量
$lotSz = $searchmarkets['lotSz']; //精度
$precision = $searchmarkets['precision']; //精度
$amount = $amountInUSDT / $c0_close / $ctVal; // 計算下單數量
// 輸出變數的值
echo "合約面值 (ctVal): " . $ctVal . "
";
echo "最小下單數量 (minSz): " . $minSz . "
";
echo "精度 (lotSz): " . $lotSz . "
";
echo "精度 (precision): " . $precision . "
";
echo "下單數量 (amount): " . $amount . "
";
$final_amount = round($amount, $precision); //最後下單金額
echo "最後下單顆數 (final_amount): " . $final_amount . "
";
$min_amount = $c0_close * $ctVal * $minSz;
echo "最小下單金額 (final_amount): " . $min_amount . "
";
// 判斷條件並設置 action
if ($order_signal === "LONG" && $marketposition !== 1) {
echo "LONG
";
try {
echo "情況3/3:close
";
if ($position != 0) {
$order = http_req("/api/v5/trade/close-position", "POST", array("instId" => $instId, "mgnMode" => "cross"));
print_r($order);
echo "
";
}
} catch (Exception $e) {
echo 'Error: ' . $e->getMessage() . "
\n";
}
$final_amount = round($amount, $precision); //最後下單金額
if ($final_amount >= $minSz) {
try {
echo "情況1/3:long
";
$order = http_req("/api/v5/trade/order", "POST", array("instId" => $instId, "tdMode" => "cross", "side" => "buy", "ordType" => "market", "sz" => $final_amount));
print_r($order);
echo "
";
} catch (Exception $e) {
echo 'Error: ' . $e->getMessage() . "
\n";
}
} else {
echo "下單數量 < $minSz
";
}
} elseif ($order_signal === "SHORT" && $marketposition !== -1) {
echo "SHORT
";
try {
echo "情況3/3:close
";
if ($position != 0) {
$order = http_req("/api/v5/trade/close-position", "POST", array("instId" => $instId, "mgnMode" => "cross"));
print_r($order);
echo "
";
}
} catch (Exception $e) {
echo 'Error: ' . $e->getMessage() . "
\n";
}
$final_amount = round($amount, $precision); //最後下單金額
if ($final_amount >= $minSz) {
try {
echo "情況2/3:short
";
$order = http_req("/api/v5/trade/order", "POST", array("instId" => $instId, "tdMode" => "cross", "side" => "sell", "ordType" => "market", "sz" => $final_amount));
print_r($order);
echo "
";
} catch (Exception $e) {
echo 'Error: ' . $e->getMessage() . "
\n";
}
} else {
echo "下單數量 < $minSz
";
}
} else {
echo "沒訊號
";
}
} catch (\Exception $e) {
echo '錯誤: ' . $e->getMessage() . "\n"; // 顯示錯誤訊息
}
}
$execution_time = microtime(true) - $start_time; // 計算執行時間
echo "程式執行時間: $execution_time 秒
"; // 輸出執行時間
// 取得緩衝區內容
$content = ob_get_contents();
$currentHour = (int)date("H");
// 僅在當前時間為 0、8 或 16 時儲存
if (in_array($currentHour, [0, 8, 16], true)) {
$fileName = __DIR__ . "/../log/" . basename(__FILE__) . "_" . $currentHour . ".html";
if (file_put_contents($fileName, $content) !== false) {
echo "內容已成功保存至 $fileName";
} else {
echo "儲存失敗,請檢查檔案路徑或權限。";
}
} else {
echo "目前時間非 0、8 或 16,未進行儲存。";
}
//基底curl
function http_req($endpoint, $method, $params)
{
global $apiKey, $secret, $passphrase;
$curl = curl_init();
if ($method == "GET") {
$endpoint .= '?' . $params;
$body = ''; // 如果請求沒有主體,將為空字符串
}
$timestamp = gmdate('Y-m-d\TH:i:s\Z'); // 獲取當前時間戳,不包含毫秒
if ($method == "POST") {
$body = json_encode($params);
//$body = ''; // 如果請求沒有主體,將為空字符串
}
$params_for_signature = $timestamp . $method . $endpoint . $body;
$signature = base64_encode(hash_hmac('sha256', $params_for_signature, $secret, true));
curl_setopt_array($curl, array(
CURLOPT_URL => "https://www.okx.com" . $endpoint,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => array(
"OK-ACCESS-KEY: $apiKey",
"OK-ACCESS-SIGN: $signature",
"OK-ACCESS-TIMESTAMP: $timestamp",
"OK-ACCESS-PASSPHRASE: $passphrase",
"Content-Type: application/json"
),
));
if ($method == "GET") {
curl_setopt($curl, CURLOPT_HTTPGET, true);
}
$data = json_decode(curl_exec($curl), true);
//echo "";
//print_r($data);
//echo "";
curl_close($curl);
return $data;
}
//全部持倉
function fetch_positions()
{
$endpoint = "/api/v5/account/positions";
$method = "GET";
$params = "instType=SWAP";
$data = http_req($endpoint, $method, $params);
return $data; // 如果未找到目標儀器,則返回錯誤訊息
}
//單一 symbol 持倉
function searchposition($instId)
{
global $fetch_positions;
//echo "";
//print_r($fetch_positions);
//echo "";
foreach ($fetch_positions['data'] as $posData) { // 使用不同的名稱,避免衝突
if ($posData['instId'] === $instId) {
$pos = $posData['pos'];
return $pos;
}
}
return null; // 如果未找到则返回 null
}
//單一 symbol 市場資訊
function searchmarkets($instId)
{
global $markets;
foreach ($markets['data'] as $market) {
if ($market['instId'] === $instId) {
$precision = -log10($market['lotSz']); //精度
$result = array(
'ctVal' => $market['ctVal'],
'minSz' => $market['minSz'],
'lotSz' => $market['lotSz'],
'precision' => $precision,
);
return $result; // 返回包含 ctVal、minSz 和 lotSz 值的陣列
}
}
return "Instrument not found"; // 如果未找到目標儀器,則返回錯誤訊息
}
//totalEq
function totalEq()
{
$endpoint = "/api/v5/account/balance";
$method = "GET";
$params = 'null';
$data = http_req($endpoint, $method, $params);
$totalEq = $data['data'][0]['totalEq'];
return $totalEq; // 如果未找到目標儀器,則返回錯誤訊息
}
//基礎資料
function load_markets()
{
$endpoint = "/api/v5/public/instruments";
$method = "GET";
$params = "instType=SWAP";
$data = http_req($endpoint, $method, $params);
return $data; // 如果未找到目標儀器,則返回錯誤訊息
}
/**
* 獲取 OKX 交易所的所有交易對
*/
function getOkxPairs()
{
global $exchange;
try {
// 獲取所有交易對的市場資料
$markets = $exchange->load_markets();
// echo "";
// print_r($markets);
// echo "";
// 檢查資料是否正確返回
if (empty($markets)) {
throw new Exception("無法從 OKX 獲取市場資料");
}
$filteredMarkets = []; // 用於存放篩選後的交易對資料
$baseCurrencies = []; // 儲存基礎貨幣清單
// 篩選所有以 :USDT 結尾且不包含 USDC 的交易對並收集相關資料
foreach ($markets as $symbol => $market) {
if (substr($symbol, -5) === ':USDT' && strpos($symbol, 'USDC') === false) { // 排除包含 USDC 的交易對
$createdTime = isset($market['created']) ? $market['created'] : 0; // 默認為 0,如果沒有創建時間
$filteredMarkets[] = [
'symbol' => $symbol,
'base' => $market['base'],
'created' => $createdTime,
];
}
}
// 根據創建時間進行排序,最近的交易對排在最前面
usort($filteredMarkets, function ($a, $b) {
return $a['created'] - $b['created'];
});
// 根據 filteredMarkets 構建基礎貨幣清單
foreach ($filteredMarkets as $market) {
$baseCurrencies[] = $market['base'];
}
// 去除重複的基礎貨幣,但保留出現順序
$baseCurrencies = array_values(array_unique($baseCurrencies));
// 將 coinList 儲存為陣列
$coinList = $baseCurrencies; // 現在 $coinList 是一個基礎貨幣的陣列
// 返回 coinList 陣列
return $coinList;
} catch (Exception $e) {
echo '錯誤: ' . $e->getMessage();
}
}
?>