jkpark 2.3.1 → 2.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/dist/index.js +208 -167
- package/package.json +1 -1
- package/plugins/gcp/plugin.json +0 -1
- package/plugins/gcp/skills/gcloud-compute-automation/SKILL.md +0 -124
- package/plugins/trading/plugin.json +0 -1
- package/plugins/trading/skills/binance-trader/SKILL.md +0 -48
- package/plugins/trading/skills/binance-trader/assets/.env.example +0 -9
- package/plugins/trading/skills/binance-trader/references/api_usage.md +0 -65
- package/plugins/trading/skills/binance-trader/scripts/async_sockets.py +0 -29
- package/plugins/trading/skills/binance-trader/scripts/basic_ops.py +0 -31
- package/plugins/trading/skills/binance-trader/scripts/historical_data.py +0 -28
- package/plugins/trading/skills/binance-trader/scripts/order_examples.py +0 -48
package/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# jkpark
|
|
2
|
+
|
|
3
|
+
To install dependencies:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
bun install
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
To run:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bun run src/index.ts
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This project was created using `bun init` in bun v1.3.9. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
package/dist/index.js
CHANGED
|
@@ -22714,7 +22714,7 @@ var {
|
|
|
22714
22714
|
} = import__.default;
|
|
22715
22715
|
|
|
22716
22716
|
// src/index.ts
|
|
22717
|
-
import
|
|
22717
|
+
import path6 from "path";
|
|
22718
22718
|
import { fileURLToPath } from "url";
|
|
22719
22719
|
|
|
22720
22720
|
// node_modules/@inquirer/core/dist/lib/key.js
|
|
@@ -25687,99 +25687,58 @@ var dist_default14 = inquirer;
|
|
|
25687
25687
|
// src/commands/install.ts
|
|
25688
25688
|
var import_fs_extra = __toESM(require_lib4(), 1);
|
|
25689
25689
|
import path4 from "path";
|
|
25690
|
-
import
|
|
25690
|
+
import fs2 from "fs";
|
|
25691
25691
|
|
|
25692
25692
|
// src/core/path-manager.ts
|
|
25693
25693
|
import path2 from "path";
|
|
25694
25694
|
import os2 from "os";
|
|
25695
|
-
import fs from "fs";
|
|
25696
25695
|
|
|
25697
25696
|
class PathManager {
|
|
25698
|
-
static
|
|
25699
|
-
return path2.join(os2.homedir(), ".openclaw");
|
|
25697
|
+
static getOpenClawWorkspaceRoot() {
|
|
25698
|
+
return path2.join(os2.homedir(), ".openclaw", "workspace", "skills");
|
|
25700
25699
|
}
|
|
25701
|
-
static
|
|
25702
|
-
return path2.join(
|
|
25700
|
+
static getAntigravityRoot(cwd) {
|
|
25701
|
+
return path2.join(cwd, ".agent", "skills");
|
|
25703
25702
|
}
|
|
25704
|
-
static
|
|
25705
|
-
return path2.join(os2.homedir(), ".
|
|
25706
|
-
}
|
|
25707
|
-
static getWorkspaces(root) {
|
|
25708
|
-
if (!fs.existsSync(root))
|
|
25709
|
-
return [];
|
|
25710
|
-
let entries;
|
|
25711
|
-
try {
|
|
25712
|
-
entries = fs.readdirSync(root);
|
|
25713
|
-
} catch (e) {
|
|
25714
|
-
return [];
|
|
25715
|
-
}
|
|
25716
|
-
return entries.filter((f) => {
|
|
25717
|
-
if (f.startsWith("."))
|
|
25718
|
-
return false;
|
|
25719
|
-
const fullPath = path2.join(root, f);
|
|
25720
|
-
try {
|
|
25721
|
-
return fs.statSync(fullPath).isDirectory();
|
|
25722
|
-
} catch {
|
|
25723
|
-
return false;
|
|
25724
|
-
}
|
|
25725
|
-
});
|
|
25703
|
+
static getJkparkSkillsRoot() {
|
|
25704
|
+
return path2.join(os2.homedir(), ".jkpark", "skills");
|
|
25726
25705
|
}
|
|
25727
25706
|
static resolveFinalPath(baseDir, relativeOrAbsolute) {
|
|
25728
25707
|
return path2.isAbsolute(relativeOrAbsolute) ? relativeOrAbsolute : path2.resolve(baseDir, relativeOrAbsolute);
|
|
25729
25708
|
}
|
|
25730
25709
|
}
|
|
25731
25710
|
|
|
25732
|
-
// src/core/
|
|
25733
|
-
import
|
|
25711
|
+
// src/core/skill-manager.ts
|
|
25712
|
+
import fs from "fs";
|
|
25734
25713
|
import path3 from "path";
|
|
25735
25714
|
|
|
25736
|
-
class
|
|
25737
|
-
|
|
25715
|
+
class SkillManager {
|
|
25716
|
+
skillsDir;
|
|
25738
25717
|
constructor(baseDir) {
|
|
25739
|
-
this.
|
|
25718
|
+
this.skillsDir = path3.join(baseDir, "skills");
|
|
25740
25719
|
}
|
|
25741
|
-
async
|
|
25742
|
-
if (!
|
|
25720
|
+
async getAllSkills() {
|
|
25721
|
+
if (!fs.existsSync(this.skillsDir))
|
|
25743
25722
|
return [];
|
|
25744
|
-
const
|
|
25745
|
-
return dirs.map((dir) => {
|
|
25746
|
-
const pluginJsonPath = path3.join(this.pluginsDir, dir, "plugin.json");
|
|
25747
|
-
let config = { name: dir, description: "No description provided" };
|
|
25748
|
-
if (fs2.existsSync(pluginJsonPath)) {
|
|
25749
|
-
try {
|
|
25750
|
-
config = { ...config, ...JSON.parse(fs2.readFileSync(pluginJsonPath, "utf8")) };
|
|
25751
|
-
} catch (e) {
|
|
25752
|
-
console.warn(`Failed to parse plugin config at ${pluginJsonPath}:`, e);
|
|
25753
|
-
}
|
|
25754
|
-
}
|
|
25755
|
-
return { ...config, value: dir };
|
|
25756
|
-
});
|
|
25757
|
-
}
|
|
25758
|
-
async getSkills(category) {
|
|
25759
|
-
const skillsDir = path3.join(this.pluginsDir, category, "skills");
|
|
25760
|
-
if (!fs2.existsSync(skillsDir))
|
|
25761
|
-
return [];
|
|
25762
|
-
const skills = fs2.readdirSync(skillsDir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
25723
|
+
const skills = fs.readdirSync(this.skillsDir, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name);
|
|
25763
25724
|
return skills.map((skill) => {
|
|
25764
|
-
const skillPath = path3.join(skillsDir, skill, "SKILL.md");
|
|
25725
|
+
const skillPath = path3.join(this.skillsDir, skill, "SKILL.md");
|
|
25765
25726
|
let description = "No description provided";
|
|
25766
|
-
if (
|
|
25767
|
-
const content =
|
|
25727
|
+
if (fs.existsSync(skillPath)) {
|
|
25728
|
+
const content = fs.readFileSync(skillPath, "utf8");
|
|
25768
25729
|
const match = content.match(/^description:\s*(.*)$/m);
|
|
25769
25730
|
if (match && match[1]) {
|
|
25770
|
-
description = match[1].trim();
|
|
25731
|
+
description = match[1].replace(/^["']|["']$/g, "").trim();
|
|
25771
25732
|
}
|
|
25772
25733
|
}
|
|
25773
25734
|
return {
|
|
25774
25735
|
name: skill,
|
|
25775
25736
|
description,
|
|
25776
|
-
value: skill
|
|
25737
|
+
value: skill,
|
|
25738
|
+
sourcePath: path3.join(this.skillsDir, skill)
|
|
25777
25739
|
};
|
|
25778
25740
|
});
|
|
25779
25741
|
}
|
|
25780
|
-
getSkillSourcePath(category, skill) {
|
|
25781
|
-
return path3.join(this.pluginsDir, category, "skills", skill);
|
|
25782
|
-
}
|
|
25783
25742
|
}
|
|
25784
25743
|
|
|
25785
25744
|
// src/commands/install.ts
|
|
@@ -25787,39 +25746,66 @@ async function runInstallWizard(projectRoot) {
|
|
|
25787
25746
|
console.log(`
|
|
25788
25747
|
\uD83D\uDC3E jkpark 설치 마법사에 오신 걸 환영합니다!
|
|
25789
25748
|
`);
|
|
25790
|
-
const
|
|
25791
|
-
const categoryChoices = await pluginManager.getCategories();
|
|
25792
|
-
if (categoryChoices.length === 0) {
|
|
25793
|
-
console.log("❌ 설치 가능한 플러그인이 없습니다. plugins 폴더를 확인해 주세요.");
|
|
25794
|
-
return;
|
|
25795
|
-
}
|
|
25749
|
+
const cwd = process.cwd();
|
|
25796
25750
|
const { targetType } = await dist_default14.prompt([
|
|
25797
25751
|
{
|
|
25798
|
-
type: "
|
|
25752
|
+
type: "select",
|
|
25799
25753
|
name: "targetType",
|
|
25800
25754
|
message: "설치할 서비스(Target)를 선택하세요:",
|
|
25801
25755
|
choices: [
|
|
25802
|
-
{ name: "
|
|
25803
|
-
{ name: "
|
|
25804
|
-
{ name: "
|
|
25756
|
+
{ name: "openclaw (workspace)", value: "openclaw" },
|
|
25757
|
+
{ name: "antigravity (workspace)", value: "antigravity" },
|
|
25758
|
+
{ name: "custom path", value: "custom" }
|
|
25805
25759
|
]
|
|
25806
25760
|
}
|
|
25807
25761
|
]);
|
|
25808
|
-
|
|
25762
|
+
let targetPath = "";
|
|
25763
|
+
if (targetType === "openclaw") {
|
|
25764
|
+
targetPath = PathManager.getOpenClawWorkspaceRoot();
|
|
25765
|
+
} else if (targetType === "antigravity") {
|
|
25766
|
+
targetPath = PathManager.getAntigravityRoot(cwd);
|
|
25767
|
+
} else {
|
|
25768
|
+
const { customPath } = await dist_default14.prompt([
|
|
25769
|
+
{
|
|
25770
|
+
type: "input",
|
|
25771
|
+
name: "customPath",
|
|
25772
|
+
message: "설치 경로를 입력하세요:",
|
|
25773
|
+
validate: (input) => input.trim() !== "" ? true : "경로를 입력해야 합니다."
|
|
25774
|
+
}
|
|
25775
|
+
]);
|
|
25776
|
+
targetPath = PathManager.resolveFinalPath(cwd, customPath);
|
|
25777
|
+
}
|
|
25778
|
+
console.log(`
|
|
25779
|
+
\uD83D\uDCCD 설정된 Target Path: ${targetPath}`);
|
|
25780
|
+
const { pathConfirm } = await dist_default14.prompt([
|
|
25809
25781
|
{
|
|
25810
|
-
type: "
|
|
25811
|
-
name: "
|
|
25812
|
-
message: "
|
|
25813
|
-
|
|
25814
|
-
name: `${c.name.padEnd(15)} - ${c.description}`,
|
|
25815
|
-
value: c.value
|
|
25816
|
-
}))
|
|
25782
|
+
type: "confirm",
|
|
25783
|
+
name: "pathConfirm",
|
|
25784
|
+
message: "위 경로에 설치하시겠습니까?",
|
|
25785
|
+
default: true
|
|
25817
25786
|
}
|
|
25818
25787
|
]);
|
|
25819
|
-
|
|
25820
|
-
if (skills.length === 0) {
|
|
25788
|
+
if (!pathConfirm) {
|
|
25821
25789
|
console.log(`
|
|
25822
|
-
|
|
25790
|
+
❌ 설치가 취소되었습니다.`);
|
|
25791
|
+
return;
|
|
25792
|
+
}
|
|
25793
|
+
const { installOption } = await dist_default14.prompt([
|
|
25794
|
+
{
|
|
25795
|
+
type: "select",
|
|
25796
|
+
name: "installOption",
|
|
25797
|
+
message: "설치 옵션을 선택하세요:",
|
|
25798
|
+
choices: [
|
|
25799
|
+
{ name: "Option 1: 직접 설치 (타겟 폴더에 직접 복사)", value: "direct" },
|
|
25800
|
+
{ name: "Option 2: 심볼릭 링크로 설치 (~/.jkpark/skills 에 설치 후 링크 생성)", value: "symlink" }
|
|
25801
|
+
]
|
|
25802
|
+
}
|
|
25803
|
+
]);
|
|
25804
|
+
const skillManager = new SkillManager(projectRoot);
|
|
25805
|
+
const allSkills = await skillManager.getAllSkills();
|
|
25806
|
+
if (allSkills.length === 0) {
|
|
25807
|
+
console.log(`
|
|
25808
|
+
⚠️ 설치 가능한 스킬이 없습니다.`);
|
|
25823
25809
|
return;
|
|
25824
25810
|
}
|
|
25825
25811
|
const { selectedSkills } = await dist_default14.prompt([
|
|
@@ -25827,119 +25813,174 @@ async function runInstallWizard(projectRoot) {
|
|
|
25827
25813
|
type: "checkbox",
|
|
25828
25814
|
name: "selectedSkills",
|
|
25829
25815
|
message: "설치할 스킬들을 선택하세요 (Space로 선택, Enter로 완료):",
|
|
25830
|
-
choices:
|
|
25831
|
-
|
|
25832
|
-
|
|
25833
|
-
|
|
25816
|
+
choices: allSkills.map((s) => {
|
|
25817
|
+
const desc = s.description.length > 65 ? s.description.substring(0, 65) + "..." : s.description;
|
|
25818
|
+
return {
|
|
25819
|
+
name: `${s.value.padEnd(25)} - ${desc}`,
|
|
25820
|
+
value: s.value
|
|
25821
|
+
};
|
|
25822
|
+
}),
|
|
25823
|
+
loop: false,
|
|
25834
25824
|
validate: (answer) => answer.length > 0 ? true : "최소 하나 이상의 스킬을 선택해야 합니다."
|
|
25835
25825
|
}
|
|
25836
25826
|
]);
|
|
25837
|
-
let rootPath;
|
|
25838
|
-
if (targetType === "openclaw") {
|
|
25839
|
-
rootPath = PathManager.getOpenClawRoot();
|
|
25840
|
-
} else if (targetType === "claude") {
|
|
25841
|
-
rootPath = PathManager.getClaudeRoot();
|
|
25842
|
-
} else {
|
|
25843
|
-
rootPath = PathManager.getGitHubRoot();
|
|
25844
|
-
}
|
|
25845
|
-
const workspaces = PathManager.getWorkspaces(rootPath);
|
|
25846
|
-
const scopeChoices = [
|
|
25847
|
-
{ name: "Current Directory (현재 프로젝트)", value: process.cwd() }
|
|
25848
|
-
];
|
|
25849
|
-
if (targetType === "openclaw") {
|
|
25850
|
-
scopeChoices.push({ name: `Shared Skills (모든 에이전트 공유: ${path4.join(rootPath, "skills")})`, value: path4.join(rootPath, "skills") });
|
|
25851
|
-
} else if (targetType === "claude") {
|
|
25852
|
-
scopeChoices.push({ name: `Global Skills (~/.claude/skills)`, value: path4.join(rootPath, "skills") });
|
|
25853
|
-
} else if (targetType === "github") {
|
|
25854
|
-
scopeChoices.push({ name: `GitHub Extensions (~/.config/gh/extensions)`, value: path4.join(rootPath, "extensions") });
|
|
25855
|
-
}
|
|
25856
|
-
scopeChoices.push(...workspaces.map((ws) => ({ name: `Workspace: ${ws}`, value: path4.join(rootPath, ws) })));
|
|
25857
|
-
scopeChoices.push({ name: "Custom Path (직접 입력)", value: "custom" });
|
|
25858
|
-
const { scope } = await dist_default14.prompt([
|
|
25859
|
-
{
|
|
25860
|
-
type: "list",
|
|
25861
|
-
name: "scope",
|
|
25862
|
-
message: `${targetType} 설치 범위를 선택하세요 (Default: Current Directory):`,
|
|
25863
|
-
choices: scopeChoices,
|
|
25864
|
-
default: 0
|
|
25865
|
-
}
|
|
25866
|
-
]);
|
|
25867
|
-
let finalTargetDir;
|
|
25868
|
-
if (scope === "custom") {
|
|
25869
|
-
const { customPath } = await dist_default14.prompt([
|
|
25870
|
-
{
|
|
25871
|
-
type: "input",
|
|
25872
|
-
name: "customPath",
|
|
25873
|
-
message: "설치 경로를 입력하세요:",
|
|
25874
|
-
validate: (input) => input.trim() !== "" ? true : "경로를 입력해야 합니다."
|
|
25875
|
-
}
|
|
25876
|
-
]);
|
|
25877
|
-
finalTargetDir = PathManager.resolveFinalPath(process.cwd(), customPath);
|
|
25878
|
-
} else {
|
|
25879
|
-
finalTargetDir = scope;
|
|
25880
|
-
}
|
|
25881
|
-
const skillsBaseDir = targetType === "github" && scope.endsWith("extensions") ? finalTargetDir : path4.join(finalTargetDir, "skills");
|
|
25882
25827
|
console.log(`
|
|
25883
|
-
\uD83D\
|
|
25884
|
-
console.log(
|
|
25885
|
-
|
|
25886
|
-
`);
|
|
25887
|
-
const { proceed } = await dist_default14.prompt([
|
|
25828
|
+
\uD83D\uDEE0️ 선택된 스킬 목록:`);
|
|
25829
|
+
selectedSkills.forEach((s) => console.log(` - ${s}`));
|
|
25830
|
+
const { skillConfirm } = await dist_default14.prompt([
|
|
25888
25831
|
{
|
|
25889
25832
|
type: "confirm",
|
|
25890
|
-
name: "
|
|
25891
|
-
message: "위
|
|
25833
|
+
name: "skillConfirm",
|
|
25834
|
+
message: "위 스킬들을 설치하시겠습니까?",
|
|
25892
25835
|
default: true
|
|
25893
25836
|
}
|
|
25894
25837
|
]);
|
|
25895
|
-
if (
|
|
25838
|
+
if (!skillConfirm) {
|
|
25896
25839
|
console.log(`
|
|
25840
|
+
❌ 설치가 취소되었습니다.`);
|
|
25841
|
+
return;
|
|
25842
|
+
}
|
|
25843
|
+
console.log(`
|
|
25897
25844
|
\uD83D\uDE80 설치를 시작합니다...`);
|
|
25898
|
-
|
|
25899
|
-
|
|
25845
|
+
const jkparkSkillsRoot = PathManager.getJkparkSkillsRoot();
|
|
25846
|
+
if (installOption === "direct") {
|
|
25847
|
+
if (!fs2.existsSync(targetPath)) {
|
|
25848
|
+
fs2.mkdirSync(targetPath, { recursive: true });
|
|
25849
|
+
}
|
|
25850
|
+
} else {
|
|
25851
|
+
if (!fs2.existsSync(jkparkSkillsRoot)) {
|
|
25852
|
+
fs2.mkdirSync(jkparkSkillsRoot, { recursive: true });
|
|
25853
|
+
}
|
|
25854
|
+
if (!fs2.existsSync(targetPath)) {
|
|
25855
|
+
fs2.mkdirSync(targetPath, { recursive: true });
|
|
25900
25856
|
}
|
|
25901
|
-
|
|
25902
|
-
|
|
25903
|
-
|
|
25857
|
+
}
|
|
25858
|
+
for (const skillValue of selectedSkills) {
|
|
25859
|
+
const skillObj = allSkills.find((s) => s.value === skillValue);
|
|
25860
|
+
if (!skillObj || !skillObj.sourcePath)
|
|
25861
|
+
continue;
|
|
25862
|
+
if (installOption === "direct") {
|
|
25863
|
+
const destDir = path4.join(targetPath, skillObj.name);
|
|
25864
|
+
try {
|
|
25865
|
+
console.log(`- [${skillValue}] 직접 복사 중...`);
|
|
25866
|
+
await import_fs_extra.default.copy(skillObj.sourcePath, destDir);
|
|
25867
|
+
console.log(` ✅ [${skillValue}] 복사 완료`);
|
|
25868
|
+
} catch (err) {
|
|
25869
|
+
console.error(` ❌ [${skillValue}] 복사 실패:`, err.message);
|
|
25870
|
+
}
|
|
25871
|
+
} else {
|
|
25872
|
+
const baseDestDir = path4.join(jkparkSkillsRoot, skillObj.name);
|
|
25873
|
+
const symlinkDestDir = path4.join(targetPath, skillObj.name);
|
|
25904
25874
|
try {
|
|
25905
|
-
console.log(`- [${
|
|
25906
|
-
await import_fs_extra.default.copy(
|
|
25907
|
-
console.log(
|
|
25875
|
+
console.log(`- [${skillValue}] ~/.jkpark/skills에 복사 중...`);
|
|
25876
|
+
await import_fs_extra.default.copy(skillObj.sourcePath, baseDestDir);
|
|
25877
|
+
console.log(`- [${skillValue}] 심볼릭 링크 생성 중...`);
|
|
25878
|
+
if (fs2.existsSync(symlinkDestDir)) {
|
|
25879
|
+
fs2.rmSync(symlinkDestDir, { recursive: true, force: true });
|
|
25880
|
+
}
|
|
25881
|
+
const symlinkType = process.platform === "win32" ? "junction" : "dir";
|
|
25882
|
+
fs2.symlinkSync(baseDestDir, symlinkDestDir, symlinkType);
|
|
25883
|
+
console.log(` ✅ [${skillValue}] 링크 설치 완료 (${symlinkType} 방식)`);
|
|
25908
25884
|
} catch (err) {
|
|
25909
|
-
console.error(` ❌ [${
|
|
25885
|
+
console.error(` ❌ [${skillValue}] 설치 실패:`, err.message);
|
|
25910
25886
|
}
|
|
25911
25887
|
}
|
|
25912
|
-
console.log(`
|
|
25913
|
-
✅ 모든 작업이 완료되었습니다. 형, 설치가 끝났어! \uD83D\uDC3E`);
|
|
25914
|
-
} else {
|
|
25915
|
-
console.log(`
|
|
25916
|
-
❌ 설치가 취소되었습니다.`);
|
|
25917
25888
|
}
|
|
25889
|
+
console.log(`
|
|
25890
|
+
✅ 모든 작업이 완료되었습니다! \uD83D\uDC3E`);
|
|
25918
25891
|
}
|
|
25919
25892
|
async function runListCommand(projectRoot) {
|
|
25920
|
-
const
|
|
25921
|
-
const
|
|
25893
|
+
const skillManager = new SkillManager(projectRoot);
|
|
25894
|
+
const allSkills = await skillManager.getAllSkills();
|
|
25922
25895
|
console.log(`
|
|
25923
|
-
\uD83D\uDCE6 사용 가능한
|
|
25896
|
+
\uD83D\uDCE6 사용 가능한 스킬 목록:
|
|
25924
25897
|
`);
|
|
25925
|
-
|
|
25926
|
-
console.log(
|
|
25927
|
-
|
|
25928
|
-
|
|
25929
|
-
|
|
25898
|
+
if (allSkills.length === 0) {
|
|
25899
|
+
console.log(" ⚠️ 설치 가능한 스킬이 없습니다.");
|
|
25900
|
+
return;
|
|
25901
|
+
}
|
|
25902
|
+
for (const skill of allSkills) {
|
|
25903
|
+
const desc = skill.description.length > 65 ? skill.description.substring(0, 65) + "..." : skill.description;
|
|
25904
|
+
console.log(` - ${skill.name}: ${desc}`);
|
|
25905
|
+
}
|
|
25906
|
+
console.log("");
|
|
25907
|
+
}
|
|
25908
|
+
|
|
25909
|
+
// src/commands/clean.ts
|
|
25910
|
+
import fs3 from "fs";
|
|
25911
|
+
import path5 from "path";
|
|
25912
|
+
var import_fs_extra2 = __toESM(require_lib4(), 1);
|
|
25913
|
+
async function runCleanCommand() {
|
|
25914
|
+
console.log(`
|
|
25915
|
+
\uD83E\uDDF9 jkpark 캐시 정리 마법사에 오신 걸 환영합니다!
|
|
25916
|
+
`);
|
|
25917
|
+
const jkparkSkillsRoot = PathManager.getJkparkSkillsRoot();
|
|
25918
|
+
const openClawTarget = PathManager.getOpenClawWorkspaceRoot();
|
|
25919
|
+
const antigravityTarget = PathManager.getAntigravityRoot(process.cwd());
|
|
25920
|
+
console.log(`\uD83D\uDCCD 캐시 폴더 경로: ${jkparkSkillsRoot}`);
|
|
25921
|
+
console.log(`\uD83D\uDCCD 타겟 폴더 (openclaw): ${openClawTarget}`);
|
|
25922
|
+
console.log(`\uD83D\uDCCD 타겟 폴더 (antigravity): ${antigravityTarget}`);
|
|
25923
|
+
const { confirmClean } = await dist_default14.prompt([
|
|
25924
|
+
{
|
|
25925
|
+
type: "confirm",
|
|
25926
|
+
name: "confirmClean",
|
|
25927
|
+
message: `위 경로들의 스킬 캐시 공간을 비우고 각 타겟 폴더에 생성된 모든 심볼릭 링크(스킬 연결)를 삭제하시겠습니까?
|
|
25928
|
+
(이 작업은 되돌릴 수 없으며 설치된 스킬 연결이 해제됩니다.)`,
|
|
25929
|
+
default: false
|
|
25930
25930
|
}
|
|
25931
|
-
|
|
25931
|
+
]);
|
|
25932
|
+
if (!confirmClean) {
|
|
25933
|
+
console.log(`
|
|
25934
|
+
❌ 캐시 비우기가 취소되었습니다.`);
|
|
25935
|
+
return;
|
|
25936
|
+
}
|
|
25937
|
+
try {
|
|
25938
|
+
console.log(`
|
|
25939
|
+
\uD83D\uDDD1️ 원본 캐시 삭제 중...`);
|
|
25940
|
+
if (fs3.existsSync(jkparkSkillsRoot)) {
|
|
25941
|
+
import_fs_extra2.default.emptyDirSync(jkparkSkillsRoot);
|
|
25942
|
+
console.log(` ✨ [${jkparkSkillsRoot}] 내부 비우기 완료`);
|
|
25943
|
+
} else {
|
|
25944
|
+
console.log(` ✅ 캐시 폴더가 이미 비어있거나 없습니다.`);
|
|
25945
|
+
}
|
|
25946
|
+
console.log(`
|
|
25947
|
+
\uD83D\uDDD1️ 타겟 심볼릭 링크 삭제 중...`);
|
|
25948
|
+
const targets = [openClawTarget, antigravityTarget];
|
|
25949
|
+
for (const target of targets) {
|
|
25950
|
+
if (fs3.existsSync(target)) {
|
|
25951
|
+
const items = fs3.readdirSync(target);
|
|
25952
|
+
for (const item of items) {
|
|
25953
|
+
const itemPath = path5.join(target, item);
|
|
25954
|
+
try {
|
|
25955
|
+
const stat = fs3.lstatSync(itemPath);
|
|
25956
|
+
if (stat.isSymbolicLink()) {
|
|
25957
|
+
fs3.rmSync(itemPath, { recursive: true, force: true });
|
|
25958
|
+
console.log(` \uD83D\uDD17 링크 삭제됨: ${itemPath}`);
|
|
25959
|
+
}
|
|
25960
|
+
} catch (e) {
|
|
25961
|
+
console.log(` ❌ 링크 삭제 실패: ${itemPath} (${e.message})`);
|
|
25962
|
+
}
|
|
25963
|
+
}
|
|
25964
|
+
}
|
|
25965
|
+
}
|
|
25966
|
+
console.log(`
|
|
25967
|
+
✨ 캐시 정리 및 링크 해제 작업이 깔끔하게 완료되었습니다!`);
|
|
25968
|
+
} catch (error) {
|
|
25969
|
+
console.error(`
|
|
25970
|
+
❌ 삭제 프로세스 중 에러가 발생했습니다:`, error.message);
|
|
25932
25971
|
}
|
|
25933
25972
|
}
|
|
25934
25973
|
|
|
25935
25974
|
// src/index.ts
|
|
25936
25975
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
25937
|
-
var __dirname2 =
|
|
25938
|
-
var projectRoot = process.env.JKPARK_CLI_ROOT ||
|
|
25976
|
+
var __dirname2 = path6.dirname(__filename2);
|
|
25977
|
+
var projectRoot = process.env.JKPARK_CLI_ROOT || path6.join(__dirname2, "..");
|
|
25939
25978
|
var program2 = new Command;
|
|
25940
|
-
|
|
25979
|
+
var VERSION = "2.3.2";
|
|
25980
|
+
program2.name("jkpark").description("JK Park의 개인용 패키지 관리 도구").version(VERSION);
|
|
25941
25981
|
program2.command("install").description("패키지 설치 마법사를 실행합니다").action(() => runInstallWizard(projectRoot));
|
|
25942
25982
|
program2.command("list").description("사용 가능한 모든 플러그인과 스킬을 나열합니다").action(() => runListCommand(projectRoot));
|
|
25983
|
+
program2.command("clean").description("설치된 로컬 스킬 캐시(~/.jkpark/skills)를 모두 삭제하여 초기화합니다").action(() => runCleanCommand());
|
|
25943
25984
|
if (!process.argv.slice(2).length) {
|
|
25944
25985
|
program2.outputHelp();
|
|
25945
25986
|
} else {
|
package/package.json
CHANGED
package/plugins/gcp/plugin.json
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"name": "GCP Tools", "description": "Google Cloud Platform automation skills"}
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: gcloud-compute-automation
|
|
3
|
-
description: "Automates Google Cloud Compute Engine instance management including setup, instance creation, and SSH connection. Use this skill when the user needs to: (1) Install or configure the gcloud CLI, (2) Create new VM instances, or (3) Connect to existing instances via SSH."
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# GCloud Compute Automation
|
|
7
|
-
|
|
8
|
-
This skill provides a consolidated guide for managing Google Cloud Compute Engine instances directly using the `gcloud` CLI.
|
|
9
|
-
|
|
10
|
-
## 1. Setup Guide
|
|
11
|
-
|
|
12
|
-
If `gcloud` is not yet installed or configured, follow these steps:
|
|
13
|
-
|
|
14
|
-
### Installation
|
|
15
|
-
- **Debian/Ubuntu:**
|
|
16
|
-
```bash
|
|
17
|
-
# Add Package Source
|
|
18
|
-
sudo apt-get install -y apt-transport-https ca-certificates gnupg curl
|
|
19
|
-
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg
|
|
20
|
-
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | sudo tee /etc/apt/sources.list.d/google-cloud-sdk.list
|
|
21
|
-
|
|
22
|
-
# Install
|
|
23
|
-
sudo apt-get update && sudo apt-get install -y google-cloud-cli
|
|
24
|
-
```
|
|
25
|
-
- **Other Linux Distributions:**
|
|
26
|
-
```bash
|
|
27
|
-
curl https://sdk.cloud.google.com | bash
|
|
28
|
-
exec -l $SHELL
|
|
29
|
-
```
|
|
30
|
-
- **macOS:**
|
|
31
|
-
```bash
|
|
32
|
-
brew install --cask google-cloud-sdk
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### Initialization & Authentication
|
|
36
|
-
1. **Initialize gcloud**:
|
|
37
|
-
```bash
|
|
38
|
-
gcloud init
|
|
39
|
-
```
|
|
40
|
-
2. **Web-free Login**: If you are in a remote environment or prefer no browser, run:
|
|
41
|
-
```bash
|
|
42
|
-
gcloud auth login --no-launch-browser
|
|
43
|
-
```
|
|
44
|
-
- Copy the provided URL into your local browser.
|
|
45
|
-
- Authorize access and copy the verification code.
|
|
46
|
-
- Paste the code back into your terminal.
|
|
47
|
-
|
|
48
|
-
## 2. Project Management
|
|
49
|
-
|
|
50
|
-
After logging in, you must select or create a Google Cloud Project.
|
|
51
|
-
|
|
52
|
-
### A. List Existing Projects
|
|
53
|
-
Check if you already have a project to use:
|
|
54
|
-
```bash
|
|
55
|
-
gcloud projects list
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### B. Create a New Project
|
|
59
|
-
If you don't have a project, or want a new one for this VM, follow these steps:
|
|
60
|
-
1. **Create the project**:
|
|
61
|
-
```bash
|
|
62
|
-
gcloud projects create [PROJECT_ID] --name="[PROJECT_NAME]"
|
|
63
|
-
```
|
|
64
|
-
*Note: [PROJECT_ID] must be unique across all of Google Cloud.*
|
|
65
|
-
2. **Set as active**:
|
|
66
|
-
```bash
|
|
67
|
-
gcloud config set project [PROJECT_ID]
|
|
68
|
-
```
|
|
69
|
-
3. **Enable Compute Engine API**:
|
|
70
|
-
```bash
|
|
71
|
-
gcloud services enable compute.googleapis.com
|
|
72
|
-
```
|
|
73
|
-
*Note: This requires billing to be enabled for the project.*
|
|
74
|
-
|
|
75
|
-
---
|
|
76
|
-
|
|
77
|
-
## 3. VM Management Workflow
|
|
78
|
-
|
|
79
|
-
Follow this workflow to create and connect to a VM.
|
|
80
|
-
|
|
81
|
-
### Step A: Evaluate Machine Type and Cost
|
|
82
|
-
Before creating an instance, check the available machine types and their specifications to estimate costs.
|
|
83
|
-
|
|
84
|
-
1. **List Machine Types**: Display available machine types and their core/memory specs in your zone.
|
|
85
|
-
```bash
|
|
86
|
-
gcloud compute machine-types list --zones=[ZONE]
|
|
87
|
-
```
|
|
88
|
-
*Note: Costs vary by machine type. For example, `e2-micro` is generally the cheapest, while `e2-standard-4` provides more resources at a higher price.*
|
|
89
|
-
|
|
90
|
-
2. **Estimate Cost**: Use the [Google Cloud Pricing Calculator](https://cloud.google.com/products/calculator) for precise estimates based on your selected machine type and region.
|
|
91
|
-
|
|
92
|
-
### Step B: Create an Instance
|
|
93
|
-
**Mandatory**: You must provide a unique name for your instance.
|
|
94
|
-
|
|
95
|
-
```bash
|
|
96
|
-
gcloud compute instances create [MANDATORY_INSTANCE_NAME] \
|
|
97
|
-
--zone=[ZONE] \
|
|
98
|
-
--machine-type=[MACHINE_TYPE] \
|
|
99
|
-
--image-family=debian-12 \
|
|
100
|
-
--image-project=debian-cloud
|
|
101
|
-
```
|
|
102
|
-
*Tip: Common zones include `us-central1-a`, `asia-northeast3-a` (Seoul). Recommended machine types: `e2-micro` (development), `e2-small` (standard).*
|
|
103
|
-
|
|
104
|
-
### Step C: Connect via SSH
|
|
105
|
-
Once the instance is running, connect using:
|
|
106
|
-
```bash
|
|
107
|
-
gcloud compute ssh [INSTANCE_NAME] --zone=[ZONE]
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
---
|
|
111
|
-
|
|
112
|
-
## 4. Quick Reference
|
|
113
|
-
|
|
114
|
-
| Action | Command |
|
|
115
|
-
| :--- | :--- |
|
|
116
|
-
| **List Instances** | `gcloud compute instances list` |
|
|
117
|
-
| **Stop Instance** | `gcloud compute instances stop [NAME]` |
|
|
118
|
-
| **Start Instance** | `gcloud compute instances start [NAME]` |
|
|
119
|
-
| **Delete Instance** | `gcloud compute instances delete [NAME]` |
|
|
120
|
-
| **Check Config** | `gcloud config list` |
|
|
121
|
-
|
|
122
|
-
## Tips for Efficiency
|
|
123
|
-
- **Preemptible VMs:** Add `--preemptible` to the create command to save up to 80% on costs for short-lived tasks.
|
|
124
|
-
- **Static External IP:** If you need a constant IP, use `--address=[IP_NAME]`.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"name": "Trading Bot", "description": "Binance trading automation skills"}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: binance-trader
|
|
3
|
-
description: Interface with the Binance API using python-binance. Use for automated trading, account management, market data retrieval, and real-time socket streaming. Contains examples for orders, historical data, and async operations.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Binance Trader Skill
|
|
7
|
-
|
|
8
|
-
This skill provides a structured way to interact with the Binance exchange using the `python-binance` Python wrapper.
|
|
9
|
-
|
|
10
|
-
## Core Workflows
|
|
11
|
-
|
|
12
|
-
### 1. Market Data Retrieval
|
|
13
|
-
Fetch current prices, order books, or historical K-line (candle) data.
|
|
14
|
-
- **Reference**: See [references/api_usage.md](references/api_usage.md)
|
|
15
|
-
- **Example Script**: [scripts/historical_data.py](scripts/historical_data.py)
|
|
16
|
-
|
|
17
|
-
### 2. Account & Portfolio Management
|
|
18
|
-
Check balances and manage account settings.
|
|
19
|
-
- **Example Script**: [scripts/basic_ops.py](scripts/basic_ops.py)
|
|
20
|
-
|
|
21
|
-
### 3. Order Execution
|
|
22
|
-
Place Market, Limit, or OCO (One-Cancels-the-Other) orders.
|
|
23
|
-
- **Reference**: See [references/api_usage.md](references/api_usage.md)
|
|
24
|
-
- **Example Script**: [scripts/order_examples.py](scripts/order_examples.py)
|
|
25
|
-
|
|
26
|
-
### 4. Real-time Streaming (WebSockets)
|
|
27
|
-
Stream ticker updates, trade data, or account updates using `asyncio`.
|
|
28
|
-
- **Example Script**: [scripts/async_sockets.py](scripts/async_sockets.py)
|
|
29
|
-
|
|
30
|
-
## Best Practices
|
|
31
|
-
|
|
32
|
-
- **Security**: Always use environment variables (`BINANCE_API_KEY`, `BINANCE_API_SECRET` or `BINANCE_SECRET_KEY`) instead of hardcoding keys.
|
|
33
|
-
- **.env file (required)**: Place your API keys in a `.env` file at the project root. If `.env` does not exist, create it from the provided template:
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
cp skills/binance-trader/assets/.env.example .env
|
|
37
|
-
# then edit .env and fill in your keys
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Scripts shipped with this skill will automatically look for `.env` in the script directory, the `test/` folder, and the project root.
|
|
41
|
-
|
|
42
|
-
- **Error Handling**: Wrap API calls in `try-except` blocks to handle `BinanceAPIException`.
|
|
43
|
-
- **Rate Limits**: Be mindful of Binance's rate limits (weight-based).
|
|
44
|
-
- **Testnet**: Use `testnet=True` during development to avoid losing real funds.
|
|
45
|
-
|
|
46
|
-
## Resources
|
|
47
|
-
- **Repository**: [sammchardy/python-binance](https://github.com/sammchardy/python-binance)
|
|
48
|
-
- **Official Docs**: [python-binance.readthedocs.io](https://python-binance.readthedocs.io/en/latest/)
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# Binance API Guide (python-binance)
|
|
2
|
-
|
|
3
|
-
This guide covers common tasks when using the `python-binance` library.
|
|
4
|
-
|
|
5
|
-
## Setup
|
|
6
|
-
|
|
7
|
-
Install the library:
|
|
8
|
-
```bash
|
|
9
|
-
pip install python-binance python-dotenv
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
## Credentials
|
|
13
|
-
|
|
14
|
-
Create a `.env` file based on `assets/.env.example`:
|
|
15
|
-
```bash
|
|
16
|
-
cp skills/binance-trader/assets/.env.example .env
|
|
17
|
-
```
|
|
18
|
-
Edit `.env` and add your Binance API keys (required variables: `BINANCE_API_KEY` and `BINANCE_API_SECRET` — the scripts also support `BINANCE_SECRET_KEY`).
|
|
19
|
-
|
|
20
|
-
If `.env` is missing, the scripts will try to find it in the script directory, the `test/` folder, and then the project root; if you prefer, place `.env` at the project root for consistency.
|
|
21
|
-
|
|
22
|
-
## Client Initialization
|
|
23
|
-
|
|
24
|
-
```python
|
|
25
|
-
from binance import Client
|
|
26
|
-
client = Client(api_key, api_secret)
|
|
27
|
-
# For Testnet
|
|
28
|
-
# client = Client(api_key, api_secret, testnet=True)
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Common REST API Methods
|
|
32
|
-
|
|
33
|
-
| Method | Description |
|
|
34
|
-
|--------|-------------|
|
|
35
|
-
| `get_account()` | Fetch account information and balances |
|
|
36
|
-
| `get_asset_balance(asset='BTC')` | Get balance for a specific asset |
|
|
37
|
-
| `get_symbol_ticker(symbol='BTCUSDT')` | Get latest price |
|
|
38
|
-
| `get_order_book(symbol='BTCUSDT')` | Get order book depth |
|
|
39
|
-
| `get_historical_klines(symbol, interval, start_str, end_str=None)` | Get historical OHLCV data |
|
|
40
|
-
|
|
41
|
-
## Order Types
|
|
42
|
-
|
|
43
|
-
### Market Order
|
|
44
|
-
```python
|
|
45
|
-
client.create_order(symbol='BTCUSDT', side='BUY', type='MARKET', quantity=0.001)
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
### Limit Order
|
|
49
|
-
```python
|
|
50
|
-
client.create_order(symbol='BTCUSDT', side='SELL', type='LIMIT', timeInForce='GTC', quantity=0.001, price='70000')
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### OCO Order
|
|
54
|
-
```python
|
|
55
|
-
client.create_oco_order(symbol='BTCUSDT', side='SELL', quantity=0.001, price='75000', stopPrice='60000', stopLimitPrice='59900', stopLimitTimeInForce='GTC')
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## WebSockets (Async)
|
|
59
|
-
|
|
60
|
-
Use `BinanceSocketManager` for real-time data.
|
|
61
|
-
|
|
62
|
-
```python
|
|
63
|
-
from binance import AsyncClient, BinanceSocketManager
|
|
64
|
-
# See scripts/async_sockets.py for example
|
|
65
|
-
```
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
from binance import AsyncClient, BinanceSocketManager
|
|
3
|
-
import os
|
|
4
|
-
|
|
5
|
-
# Manual .env loading if dotenv is missing
|
|
6
|
-
if os.path.exists('.env'):
|
|
7
|
-
with open('.env', 'r') as f:
|
|
8
|
-
for line in f:
|
|
9
|
-
if '=' in line and not line.startswith('#'):
|
|
10
|
-
key, value = line.strip().split('=', 1)
|
|
11
|
-
os.environ[key] = value
|
|
12
|
-
|
|
13
|
-
async def main():
|
|
14
|
-
client = await AsyncClient.create(
|
|
15
|
-
api_key=os.getenv('BINANCE_API_KEY'),
|
|
16
|
-
api_secret=os.getenv('BINANCE_API_SECRET') or os.getenv('BINANCE_SECRET_KEY')
|
|
17
|
-
)
|
|
18
|
-
bsm = BinanceSocketManager(client)
|
|
19
|
-
|
|
20
|
-
# K-line socket
|
|
21
|
-
async with bsm.kline_socket(symbol='BTCUSDT') as stream:
|
|
22
|
-
for _ in range(5):
|
|
23
|
-
res = await stream.recv()
|
|
24
|
-
print(f"BTCUSDT K-line: {res['k']['c']}") # Current close price
|
|
25
|
-
|
|
26
|
-
await client.close_connection()
|
|
27
|
-
|
|
28
|
-
if __name__ == "__main__":
|
|
29
|
-
asyncio.run(main())
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
from binance import Client
|
|
2
|
-
import os
|
|
3
|
-
|
|
4
|
-
# Manual .env loading if dotenv is missing
|
|
5
|
-
env_path = '.env'
|
|
6
|
-
if not os.path.exists(env_path):
|
|
7
|
-
env_path = os.path.join(os.path.dirname(__file__), '..', '..', '.env') # From scripts/ to root
|
|
8
|
-
|
|
9
|
-
if os.path.exists(env_path):
|
|
10
|
-
with open(env_path, 'r') as f:
|
|
11
|
-
for line in f:
|
|
12
|
-
if '=' in line and not line.startswith('#'):
|
|
13
|
-
key, value = line.strip().split('=', 1)
|
|
14
|
-
os.environ[key] = value.strip('"').strip("'")
|
|
15
|
-
|
|
16
|
-
api_key = os.getenv('BINANCE_API_KEY')
|
|
17
|
-
api_secret = os.getenv('BINANCE_API_SECRET') or os.getenv('BINANCE_SECRET_KEY')
|
|
18
|
-
|
|
19
|
-
client = Client(api_key, api_secret)
|
|
20
|
-
|
|
21
|
-
# Get market depth
|
|
22
|
-
depth = client.get_order_book(symbol='BTCUSDT')
|
|
23
|
-
print(f"Bids: {depth['bids'][:3]}")
|
|
24
|
-
|
|
25
|
-
# Get account information
|
|
26
|
-
account = client.get_account()
|
|
27
|
-
print(f"Account balances: {[b for b in account['balances'] if float(b['free']) > 0]}")
|
|
28
|
-
|
|
29
|
-
# Get asset balance
|
|
30
|
-
balance = client.get_asset_balance(asset='BTC')
|
|
31
|
-
print(f"BTC Balance: {balance}")
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
from binance import Client
|
|
2
|
-
import os
|
|
3
|
-
|
|
4
|
-
client = Client() # Public API doesn't require keys
|
|
5
|
-
|
|
6
|
-
# Fetch 1 minute klines for the last day
|
|
7
|
-
klines = client.get_historical_klines("BTCUSDT", Client.KLINE_INTERVAL_1MINUTE, "1 day ago UTC")
|
|
8
|
-
|
|
9
|
-
# kline format:
|
|
10
|
-
# [
|
|
11
|
-
# [
|
|
12
|
-
# 1499040000000, # Open time
|
|
13
|
-
# "0.01634790", # Open
|
|
14
|
-
# "0.80000000", # High
|
|
15
|
-
# "0.01575800", # Low
|
|
16
|
-
# "0.01577100", # Close
|
|
17
|
-
# "148976.11427815", # Volume
|
|
18
|
-
# 1499644799999, # Close time
|
|
19
|
-
# "2434.19055334", # Quote asset volume
|
|
20
|
-
# 308, # Number of trades
|
|
21
|
-
# "1756.87402397", # Taker buy base asset volume
|
|
22
|
-
# "28.46694368", # Taker buy quote asset volume
|
|
23
|
-
# "17928899.62484339" # Ignore
|
|
24
|
-
# ]
|
|
25
|
-
# ]
|
|
26
|
-
|
|
27
|
-
for k in klines[:5]:
|
|
28
|
-
print(f"Time: {k[0]}, Open: {k[1]}, Close: {k[4]}")
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
from binance import Client
|
|
2
|
-
import os
|
|
3
|
-
|
|
4
|
-
# Manual .env loading if dotenv is missing
|
|
5
|
-
env_path = '.env'
|
|
6
|
-
if not os.path.exists(env_path):
|
|
7
|
-
env_path = os.path.join(os.path.dirname(__file__), '..', '..', '.env')
|
|
8
|
-
|
|
9
|
-
if os.path.exists(env_path):
|
|
10
|
-
with open(env_path, 'r') as f:
|
|
11
|
-
for line in f:
|
|
12
|
-
if '=' in line and not line.startswith('#'):
|
|
13
|
-
key, value = line.strip().split('=', 1)
|
|
14
|
-
os.environ[key] = value.strip('"').strip("'")
|
|
15
|
-
|
|
16
|
-
api_key = os.getenv('BINANCE_API_KEY')
|
|
17
|
-
api_secret = os.getenv('BINANCE_API_SECRET') or os.getenv('BINANCE_SECRET_KEY')
|
|
18
|
-
|
|
19
|
-
client = Client(api_key, api_secret)
|
|
20
|
-
|
|
21
|
-
# 1. Market Buy Order
|
|
22
|
-
# order = client.create_order(
|
|
23
|
-
# symbol='BNBBTC',
|
|
24
|
-
# side=Client.SIDE_BUY,
|
|
25
|
-
# type=Client.ORDER_TYPE_MARKET,
|
|
26
|
-
# quantity=100)
|
|
27
|
-
|
|
28
|
-
# 2. Limit Sell Order
|
|
29
|
-
# order = client.create_order(
|
|
30
|
-
# symbol='BNBBTC',
|
|
31
|
-
# side=Client.SIDE_SELL,
|
|
32
|
-
# type=Client.ORDER_TYPE_LIMIT,
|
|
33
|
-
# timeInForce=Client.TIME_IN_FORCE_GTC,
|
|
34
|
-
# quantity=100,
|
|
35
|
-
# price='0.0001')
|
|
36
|
-
|
|
37
|
-
# 3. OCO (One-Cancels-the-Other) Order
|
|
38
|
-
# OCO orders are useful for setting both a take-profit and a stop-loss
|
|
39
|
-
# order = client.create_oco_order(
|
|
40
|
-
# symbol='BTCUSDT',
|
|
41
|
-
# side=Client.SIDE_SELL,
|
|
42
|
-
# stopLimitTimeInForce=Client.TIME_IN_FORCE_GTC,
|
|
43
|
-
# quantity=0.001,
|
|
44
|
-
# stopPrice='60000',
|
|
45
|
-
# stopLimitPrice='59900',
|
|
46
|
-
# price='75000')
|
|
47
|
-
|
|
48
|
-
print("Example script for order types. Uncomment code blocks to use.")
|