@tradelayerprotocol/tradelayer 1.9.1
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/.claude/settings.local.json +13 -0
- package/.claude/skills/tl-algo/SKILL.md +255 -0
- package/.gitattributes +2 -0
- package/.github/workflows/publish.yaml +26 -0
- package/4mm.js +163 -0
- package/LICENSE +21 -0
- package/NPMSwapRefactor.zip +0 -0
- package/README.md +217 -0
- package/address.sh +26 -0
- package/algoAPI.js +581 -0
- package/analyzepsbt.js +92 -0
- package/apiEx.js +99 -0
- package/bb_hyperscalper.js +290 -0
- package/bbo_demo.js +111 -0
- package/buyer.js +622 -0
- package/client.js +50 -0
- package/createTxTest.js +26 -0
- package/createWallet.js +75 -0
- package/daytrader.js +531 -0
- package/decodeTest.js +69 -0
- package/fundingManager.js +144 -0
- package/index.js +4 -0
- package/listener.js +27 -0
- package/litecoreTxBuilder.js +1128 -0
- package/mmEx.js +356 -0
- package/networks.js +51 -0
- package/orderbook.js +200 -0
- package/package.json +34 -0
- package/perTradeQueue.js +36 -0
- package/projectsTLNPMTLNPM/package-lock.json +162 -0
- package/projectsTLNPMTLNPM/package.json +5 -0
- package/quick.js +32 -0
- package/quickFut.js +37 -0
- package/quickSell.js +37 -0
- package/relayerClient.js +117 -0
- package/run4mm.js +80 -0
- package/run_bbo_tracker.js +241 -0
- package/seller.js +443 -0
- package/session.js +45 -0
- package/setup-lin-ltc.sh +139 -0
- package/setup-lin.sh +203 -0
- package/setup-win-ltc.bat +108 -0
- package/setup-win.bat +167 -0
- package/spam_screamer_futures.js +222 -0
- package/tradelayer.js/.gitattributes +2 -0
- package/tradelayer.js/README.md +2 -0
- package/tradelayer.js/oldTests/activationTest.js +6 -0
- package/tradelayer.js/oldTests/base58.test.js +23 -0
- package/tradelayer.js/oldTests/base64Decode.test.js +16 -0
- package/tradelayer.js/oldTests/blocksRefactor.js +140 -0
- package/tradelayer.js/oldTests/checkVestBalance.js +25 -0
- package/tradelayer.js/oldTests/consensusHashProto.js +151 -0
- package/tradelayer.js/oldTests/contractOrderbook.js +243 -0
- package/tradelayer.js/oldTests/createPayload.js +0 -0
- package/tradelayer.js/oldTests/createTestnetAddr.js +43 -0
- package/tradelayer.js/oldTests/decode.js +205 -0
- package/tradelayer.js/oldTests/decodeTest.js +50 -0
- package/tradelayer.js/oldTests/displayTallyMap.js +19 -0
- package/tradelayer.js/oldTests/encodeDecode.js +340 -0
- package/tradelayer.js/oldTests/expressTest.js +29 -0
- package/tradelayer.js/oldTests/extractBlocksVanilla.js +214 -0
- package/tradelayer.js/oldTests/extractBlocksVanillaa.js +179 -0
- package/tradelayer.js/oldTests/extractPubkeyTest.js +60 -0
- package/tradelayer.js/oldTests/fillInputCacheProto.js +111 -0
- package/tradelayer.js/oldTests/getRawTxTest.js +22 -0
- package/tradelayer.js/oldTests/indexTest.js +26 -0
- package/tradelayer.js/oldTests/initTokensTest.js +32 -0
- package/tradelayer.js/oldTests/interfaceChild.js +129 -0
- package/tradelayer.js/oldTests/listenerChild.js +112 -0
- package/tradelayer.js/oldTests/opdecode.js +26 -0
- package/tradelayer.js/oldTests/options.js +79 -0
- package/tradelayer.js/oldTests/optxtest.js +116 -0
- package/tradelayer.js/oldTests/optxtest1.js +64 -0
- package/tradelayer.js/oldTests/oracle.test.js +32 -0
- package/tradelayer.js/oldTests/orderbook.test.js +36 -0
- package/tradelayer.js/oldTests/parsing.js +93 -0
- package/tradelayer.js/oldTests/payload.js +13 -0
- package/tradelayer.js/oldTests/persistenceUnitTest.js +23 -0
- package/tradelayer.js/oldTests/property.test.js +53 -0
- package/tradelayer.js/oldTests/propertyLevel.js +75 -0
- package/tradelayer.js/oldTests/propertyTest.js +32 -0
- package/tradelayer.js/oldTests/queryAddressTest.js +17 -0
- package/tradelayer.js/oldTests/salter.js +14 -0
- package/tradelayer.js/oldTests/tally.js +81 -0
- package/tradelayer.js/oldTests/tally.test.js +48 -0
- package/tradelayer.js/oldTests/tally2.js +124 -0
- package/tradelayer.js/oldTests/tally3.js +142 -0
- package/tradelayer.js/oldTests/tallyDiag.js +38 -0
- package/tradelayer.js/oldTests/testGetRaw.js +40 -0
- package/tradelayer.js/oldTests/testHexConvert.js +47 -0
- package/tradelayer.js/oldTests/testNewEncoding.js +96 -0
- package/tradelayer.js/oldTests/testNewEncoding2.js +113 -0
- package/tradelayer.js/oldTests/testNewEncoding3 +112 -0
- package/tradelayer.js/oldTests/testNewEncoding3.js +168 -0
- package/tradelayer.js/oldTests/testOPReturn.js +102 -0
- package/tradelayer.js/oldTests/testPayload.js +23 -0
- package/tradelayer.js/oldTests/testRaw.js +50 -0
- package/tradelayer.js/oldTests/testSendTooMuch.js +20 -0
- package/tradelayer.js/oldTests/testTxBuild +28 -0
- package/tradelayer.js/oldTests/testTxBuild.js +42 -0
- package/tradelayer.js/oldTests/tokenOrderbook.js +243 -0
- package/tradelayer.js/oldTests/txUtilsA.js +515 -0
- package/tradelayer.js/oldTests/validityUnitTest.js +53 -0
- package/tradelayer.js/oldTests/vaults.js +72 -0
- package/tradelayer.js/oldTests/volumeIndex.js +117 -0
- package/tradelayer.js/oldTests/volumeIndex2.js +88 -0
- package/tradelayer.js/output_base64.txt +1 -0
- package/tradelayer.js/package-lock.json +9967 -0
- package/tradelayer.js/package.json +61 -0
- package/tradelayer.js/server/index.js +88 -0
- package/tradelayer.js/server/litecoind.exe +0 -0
- package/tradelayer.js/src/activation.js +303 -0
- package/tradelayer.js/src/adjuster.js +77 -0
- package/tradelayer.js/src/amm.js +400 -0
- package/tradelayer.js/src/base256.js +55 -0
- package/tradelayer.js/src/base94.js +79 -0
- package/tradelayer.js/src/channels.js +1163 -0
- package/tradelayer.js/src/clearing.js +3109 -0
- package/tradelayer.js/src/clearlist.js +364 -0
- package/tradelayer.js/src/client.js +295 -0
- package/tradelayer.js/src/consensus.js +613 -0
- package/tradelayer.js/src/contractRegistry.js +964 -0
- package/tradelayer.js/src/db.js +89 -0
- package/tradelayer.js/src/init.js +24 -0
- package/tradelayer.js/src/insurance.js +347 -0
- package/tradelayer.js/src/interface.js +218 -0
- package/tradelayer.js/src/interfaceExpress.js +178 -0
- package/tradelayer.js/src/iou.js +509 -0
- package/tradelayer.js/src/listener.js +226 -0
- package/tradelayer.js/src/logic.js +1702 -0
- package/tradelayer.js/src/main.js +927 -0
- package/tradelayer.js/src/marginMap.js +2165 -0
- package/tradelayer.js/src/options.js +126 -0
- package/tradelayer.js/src/oracle.js +394 -0
- package/tradelayer.js/src/orderbook.js +4123 -0
- package/tradelayer.js/src/persistence.js +554 -0
- package/tradelayer.js/src/property.js +411 -0
- package/tradelayer.js/src/reOrg.js +41 -0
- package/tradelayer.js/src/scaling.js +145 -0
- package/tradelayer.js/src/tally.js +1275 -0
- package/tradelayer.js/src/tradeHistoryManager.js +552 -0
- package/tradelayer.js/src/txDecoder.js +584 -0
- package/tradelayer.js/src/txEncoder.js +610 -0
- package/tradelayer.js/src/txIndex.js +502 -0
- package/tradelayer.js/src/txUtils.js +1392 -0
- package/tradelayer.js/src/types.js +429 -0
- package/tradelayer.js/src/validity.js +3077 -0
- package/tradelayer.js/src/vaults.js +430 -0
- package/tradelayer.js/src/vesting.js +491 -0
- package/tradelayer.js/src/volumeIndex.js +618 -0
- package/tradelayer.js/src/walletInterface.js +220 -0
- package/tradelayer.js/src/walletListener.js +665 -0
- package/tradelayer.js/tests/256decode.js +82 -0
- package/tradelayer.js/tests/UTXOracle.js +205 -0
- package/tradelayer.js/tests/base94test.js +23 -0
- package/tradelayer.js/tests/cancelTxTest.js +62 -0
- package/tradelayer.js/tests/contractInterfaceTest.js +48 -0
- package/tradelayer.js/tests/decimalTest.js +65 -0
- package/tradelayer.js/tests/decoderTest.js +100 -0
- package/tradelayer.js/tests/deltaCount.js +47 -0
- package/tradelayer.js/tests/deltaCount2.js +60 -0
- package/tradelayer.js/tests/interfaceTest.js +37 -0
- package/tradelayer.js/tests/mainTest.js +53 -0
- package/tradelayer.js/tests/makeActivationTest.js +24 -0
- package/tradelayer.js/tests/maxHeightTest.js +49 -0
- package/tradelayer.js/tests/reverseHash.js +72 -0
- package/tradelayer.js/tests/sensitiveConsoleOutput.txt +267 -0
- package/tradelayer.js/tests/tallyTest.js +40 -0
- package/tradelayer.js/tests/testBuybacks.js +46 -0
- package/tradelayer.js/tests/testCodeHash.js +49 -0
- package/tradelayer.js/tests/testConsensusHash.js +91 -0
- package/tradelayer.js/tests/testDecode.js +30 -0
- package/tradelayer.js/tests/testEncodingLengths.js +129 -0
- package/tradelayer.js/tests/testGetTx +32 -0
- package/tradelayer.js/tests/testGetTx.js +32 -0
- package/tradelayer.js/tests/testHexHash.js +32 -0
- package/tradelayer.js/tests/testIndexHash.js +35 -0
- package/tradelayer.js/tests/testInitContracts.js +38 -0
- package/tradelayer.js/tests/testMaxConsensus.js +12 -0
- package/tradelayer.js/tests/testMaxSynth.js +44 -0
- package/tradelayer.js/tests/testMint.js +21 -0
- package/tradelayer.js/tests/testNetwork.js +33 -0
- package/tradelayer.js/tests/testOrderbookLoad.js +62 -0
- package/tradelayer.js/tests/testRebates.js +32 -0
- package/tradelayer.js/tests/testRedeem.js +22 -0
- package/tradelayer.js/tests/testTokenTrade.js +39 -0
- package/tradelayer.js/tests/testTxBuild.js +42 -0
- package/tradelayer.js/tests/testUTXOTrade.js +27 -0
- package/tradelayer.js/tests/tokenTradeHistory.js +27 -0
- package/tradelayer.js/tests/tradeFutures.js +40 -0
- package/tradelayer.js/tests/tradeHistoryExample.js +35 -0
- package/tradelayer.js/tests/tradeHistoryLoad.js +15 -0
- package/tradelayer.js/tests/txScanTest.js +134 -0
- package/tradelayer.js/tests/validateTest.js +136 -0
- package/tradelayer.js/tests/vestingTest.js +37 -0
- package/tradelayer.js/utils/activateMainnet.js +59 -0
- package/tradelayer.js/utils/activateMainnetDoge.js +63 -0
- package/tradelayer.js/utils/autocompactdb.js +23 -0
- package/tradelayer.js/utils/base64toHex.js +32 -0
- package/tradelayer.js/utils/broadcastDoge.js +38 -0
- package/tradelayer.js/utils/calcRedeem.js +19 -0
- package/tradelayer.js/utils/checkNetwork.js +27 -0
- package/tradelayer.js/utils/createAddress.js +48 -0
- package/tradelayer.js/utils/createAttestation.js +133 -0
- package/tradelayer.js/utils/createContract.js +118 -0
- package/tradelayer.js/utils/createOracle.js +94 -0
- package/tradelayer.js/utils/createwallet.js +20 -0
- package/tradelayer.js/utils/crossFuturesTrades.js +57 -0
- package/tradelayer.js/utils/crossTokenTrades.js +62 -0
- package/tradelayer.js/utils/dumpPriv.js +29 -0
- package/tradelayer.js/utils/generateChannel.js +34 -0
- package/tradelayer.js/utils/getInfo.js +21 -0
- package/tradelayer.js/utils/hardWipe.js +20 -0
- package/tradelayer.js/utils/hexTo64.js +16 -0
- package/tradelayer.js/utils/importAddress.js +28 -0
- package/tradelayer.js/utils/importpriv.js +20 -0
- package/tradelayer.js/utils/issueOracleContract.js +67 -0
- package/tradelayer.js/utils/issueTokens.js +41 -0
- package/tradelayer.js/utils/listunspent.js +66 -0
- package/tradelayer.js/utils/litecoinClient.js +30 -0
- package/tradelayer.js/utils/loadwallet.js +20 -0
- package/tradelayer.js/utils/publishOracle.js +113 -0
- package/tradelayer.js/utils/sendActivation.js +21 -0
- package/tradelayer.js/utils/sendChannelContractTrade.js +34 -0
- package/tradelayer.js/utils/sendChannelTokenTrade.js +34 -0
- package/tradelayer.js/utils/sendCommit.js +24 -0
- package/tradelayer.js/utils/sendDoge.js +62 -0
- package/tradelayer.js/utils/sendDogeMain.js +67 -0
- package/tradelayer.js/utils/sendDogeTx.js +46 -0
- package/tradelayer.js/utils/sendLTC.js +63 -0
- package/tradelayer.js/utils/sendMainnet.js +62 -0
- package/tradelayer.js/utils/sendTransfer.js +19 -0
- package/tradelayer.js/utils/sendVestTest.js +88 -0
- package/tradelayer.js/utils/sendWithdrawal.js +26 -0
- package/tradelayer.js/utils/simpleStart.js +8 -0
- package/tradelayer.js/utils/startStop.js +27 -0
- package/tradelayer.js/utils/structuredTrades.js +136 -0
- package/tradelayer.js/utils/verifySignature.js +90 -0
- package/tradelayer.js/utils/verifyWitnessAndScriptPubkey.js +41 -0
- package/tradelayer.js/utils/walletCache.js +172 -0
- package/tradelayer.js/utils/walletContractInterface.js +48 -0
- package/tradelayer.js/utils/walletFetchTxs.js +66 -0
- package/tradelayer.js/utils/walletUtils.js +97 -0
- package/tradelayer.js/utils/wipeDB.js +55 -0
- package/tradelayer.js/utils/wipeDBNotTx.js +50 -0
- package/txEncoder.js +529 -0
- package/utility.js +28 -0
- package/verifymessage.js +38 -0
- package/ws-transport.js +311 -0
package/createTxTest.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const createclient = require('tradelayer/litecoinClient.js'); // Adjust the path if necessary
|
|
2
|
+
|
|
3
|
+
// Initialize the Litecoin client
|
|
4
|
+
const client = createclient(true); // Pass 'true' for testnet, 'false' for mainnet
|
|
5
|
+
|
|
6
|
+
// Promisify the necessary client functions
|
|
7
|
+
const createRawTransactionAsync = require('util').promisify(client.createRawTransaction.bind(client));
|
|
8
|
+
|
|
9
|
+
const testTransaction = async () => {
|
|
10
|
+
//try {
|
|
11
|
+
// Example JSON that can be pasted in, replace with your transaction details
|
|
12
|
+
const inputs = [{"txid":"3b4546ad93a7d77ee684ec1ae01ad7eb3f67183459cd41da92ed0f1940eba1f1","vout":0},{"txid":"bc395db7a7a11b42e9711e192a860da2fffe64955cae6dd673e267532f328b3d","vout":1}]
|
|
13
|
+
const outputs = [{"tltc1qvlwcnwlhnja7wlj685ptwxej75mms9nyv7vuy8":0.02920059},{"tltc1q89kkgaslk0lt8l90jkl3cgwg7dkkszn73u4d2t":0.0001},{"data":"746c33302c302e3030346e796d386571756170712c302c3770732c302c312c30"}]
|
|
14
|
+
// Call getRawTransaction with the provided parameters
|
|
15
|
+
const rawTx = await createRawTransactionAsync(inputs,outputs);
|
|
16
|
+
|
|
17
|
+
// Log the raw transaction result
|
|
18
|
+
console.log("Raw Transaction Result: ", rawTx);
|
|
19
|
+
|
|
20
|
+
//} catch (error) {
|
|
21
|
+
// console.error("Error fetching raw transaction:", error.message);
|
|
22
|
+
//}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Run the test
|
|
26
|
+
testTransaction();
|
package/createWallet.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Generates a new BIP39 seed phrase and derives a default address.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* node createWallet.js [network]
|
|
7
|
+
*
|
|
8
|
+
* Examples:
|
|
9
|
+
* node createWallet.js # defaults to LTCTEST
|
|
10
|
+
* node createWallet.js LTC
|
|
11
|
+
* node createWallet.js BTC
|
|
12
|
+
*
|
|
13
|
+
* BIP44 paths:
|
|
14
|
+
* LTC / LTCTEST : m/44'/2'/0'/0/0
|
|
15
|
+
* BTC / BTCTEST : m/44'/0'/0'/0/0
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const nodeCrypto = require('crypto');
|
|
19
|
+
const bip39 = require('bip39');
|
|
20
|
+
const { BIP32Factory } = require('bip32');
|
|
21
|
+
const bitcoin = require('bitcoinjs-lib');
|
|
22
|
+
const ecc = require('tiny-secp256k1');
|
|
23
|
+
const { ECPairFactory } = require('ecpair');
|
|
24
|
+
const networks = require('./networks');
|
|
25
|
+
|
|
26
|
+
const ECPair = ECPairFactory(ecc);
|
|
27
|
+
const bip32 = BIP32Factory(ecc);
|
|
28
|
+
bitcoin.initEccLib(ecc);
|
|
29
|
+
|
|
30
|
+
const networkPaths = {
|
|
31
|
+
LTC: "m/84'/2'/0'/0",
|
|
32
|
+
LTCTEST: "m/84'/1'/0'/0",
|
|
33
|
+
BTC: "m/84'/0'/0'/0",
|
|
34
|
+
BTCTEST: "m/84'/1'/0'/0",
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const networkName = (process.argv[2] || 'LTCTEST').toUpperCase();
|
|
38
|
+
const net = networks[networkName];
|
|
39
|
+
const basePath = networkPaths[networkName];
|
|
40
|
+
if (!net || !basePath) {
|
|
41
|
+
console.error(`Unknown network "${networkName}". Choose from: ${Object.keys(networkPaths).join(', ')}`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const derivePath = `${basePath}/0`;
|
|
46
|
+
const mnemonic = bip39.generateMnemonic(256);
|
|
47
|
+
const seed = bip39.mnemonicToSeedSync(mnemonic);
|
|
48
|
+
const root = bip32.fromSeed(seed, net);
|
|
49
|
+
const child = root.derivePath(derivePath);
|
|
50
|
+
|
|
51
|
+
const { address } = bitcoin.payments.p2wpkh({
|
|
52
|
+
pubkey: Buffer.from(child.publicKey),
|
|
53
|
+
network: net,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const pubkey = Buffer.from(child.publicKey).toString('hex');
|
|
57
|
+
|
|
58
|
+
console.log('');
|
|
59
|
+
console.log('=== NEW WALLET ===');
|
|
60
|
+
console.log('');
|
|
61
|
+
console.log(`Network : ${networkName}`);
|
|
62
|
+
console.log(`Path : ${derivePath}`);
|
|
63
|
+
console.log('');
|
|
64
|
+
console.log('SEED PHRASE (write this down, do NOT share):');
|
|
65
|
+
console.log('');
|
|
66
|
+
console.log(` ${mnemonic}`);
|
|
67
|
+
console.log('');
|
|
68
|
+
console.log(`Address : ${address}`);
|
|
69
|
+
console.log(`PubKey : ${pubkey}`);
|
|
70
|
+
console.log('');
|
|
71
|
+
console.log('Fund this address, then start the API:');
|
|
72
|
+
console.log('');
|
|
73
|
+
console.log(` const ApiWrapper = require('./algoAPI');`);
|
|
74
|
+
console.log(` const api = new ApiWrapper(obURL, obPort, ${networkName.includes('TEST')}, true, '${address}', '${pubkey}', '${networkName}');`);
|
|
75
|
+
console.log('');
|
package/daytrader.js
ADDED
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* fib_sma_taker_tl.js
|
|
5
|
+
*
|
|
6
|
+
* TL-style ETF taker algo:
|
|
7
|
+
* - Signals + PnL from Alpaca ETF bars (SPY by default)
|
|
8
|
+
* - Executes ETF synth on TradeLayer via ApiWrapper
|
|
9
|
+
* - Optional hedge with Alpaca options (nearest strike put/call)
|
|
10
|
+
* - Fibonacci SMA ladder: 13, 21, 34, 55, 89, 144
|
|
11
|
+
* * We look for stacked triples like [55, 89, 144]:
|
|
12
|
+
* uptrend: SMA13 > SMA21 > ... > SMA144
|
|
13
|
+
* downtrend: SMA13 < SMA21 < ... < SMA144
|
|
14
|
+
* * For each triple [pShort, pMid, pLong], if stacked:
|
|
15
|
+
* - uptrend: SMA(pShort) > SMA(pMid) > SMA(pLong)
|
|
16
|
+
* - downtrend: SMA(pShort) < SMA(pMid) < SMA(pLong)
|
|
17
|
+
* we pick the triple with the *largest* pLong and enter
|
|
18
|
+
* on retrace to SMA(pShort) (“next one when the longer ones
|
|
19
|
+
* are above each other”).
|
|
20
|
+
*
|
|
21
|
+
* - Trailing stop using last 5 bars once PnL >= 10 bps
|
|
22
|
+
* - Hard stop -20 bps if no hedge
|
|
23
|
+
*
|
|
24
|
+
* This is example code. Backtest before real money.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const Alpaca = require('@alpacahq/alpaca-trade-api');
|
|
28
|
+
const axios = require('axios');
|
|
29
|
+
|
|
30
|
+
// ===== TL ApiWrapper (wire this to match run_bbo_tracker) =====
|
|
31
|
+
/**
|
|
32
|
+
* Adjust this import/path to match however run_bbo_tracker.js pulls ApiWrapper.
|
|
33
|
+
* If that file does something like:
|
|
34
|
+
* const { ApiWrapper } = require('./algoAPI');
|
|
35
|
+
* then keep this exactly the same.
|
|
36
|
+
*/
|
|
37
|
+
const { ApiWrapper } = require('./algoAPI'); // <--- CHECK THIS PATH
|
|
38
|
+
|
|
39
|
+
// Example env-driven TL config; mirror whatever you use in quick_env / BBO tracker.
|
|
40
|
+
const tlApi = new ApiWrapper(
|
|
41
|
+
process.env.TL_BASE_URL || 'http://127.0.0.1',
|
|
42
|
+
Number(process.env.TL_PORT || 3001),
|
|
43
|
+
process.env.TL_TEST === 'true',
|
|
44
|
+
process.env.TL_ALREADY_ON === 'true',
|
|
45
|
+
process.env.TL_ADDRESS,
|
|
46
|
+
process.env.TL_PUBKEY,
|
|
47
|
+
process.env.TL_NETWORK || 'LTCTEST'
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
// ===== Global state for TL position tracking =====
|
|
51
|
+
/**
|
|
52
|
+
* We track TL position in-process instead of querying the node:
|
|
53
|
+
* currentPosition = {
|
|
54
|
+
* side: 'long' | 'short',
|
|
55
|
+
* qty: number,
|
|
56
|
+
* entryPrice: number // from Alpaca ETF price at entry
|
|
57
|
+
* }
|
|
58
|
+
*/
|
|
59
|
+
let currentPosition = null;
|
|
60
|
+
let currentHedgeSymbol = null;
|
|
61
|
+
|
|
62
|
+
// ===== Config =====
|
|
63
|
+
const CFG = {
|
|
64
|
+
// Alpaca ETF (price + bars + hedge)
|
|
65
|
+
ETF_SYMBOL: 'SPY', // Underlying ETF on Alpaca
|
|
66
|
+
BAR_TIMEFRAME: '1Min', // Alpaca bars: 1Min, 5Min, 15Min, etc.
|
|
67
|
+
BAR_LOOKBACK_DAYS: 3,
|
|
68
|
+
BAR_LIMIT: 800,
|
|
69
|
+
|
|
70
|
+
// TL market symbol for ETF synth (whatever you use on TradeLayer)
|
|
71
|
+
TL_MARKET: 'SPY-PERP', // TODO: adjust to your TL symbol
|
|
72
|
+
|
|
73
|
+
// Fibonacci SMA ladder
|
|
74
|
+
FIB_PERIODS: [13, 21, 34, 55, 89, 144],
|
|
75
|
+
// Triples we consider for trend; we’ll pick the one with largest max period
|
|
76
|
+
FIB_TRIPLES: [
|
|
77
|
+
[13, 21, 34],
|
|
78
|
+
[21, 34, 55],
|
|
79
|
+
[34, 55, 89],
|
|
80
|
+
[55, 89, 144],
|
|
81
|
+
],
|
|
82
|
+
|
|
83
|
+
USE_OPTION_HEDGE: true,
|
|
84
|
+
HEDGE_MIN_DTE: 3,
|
|
85
|
+
HEDGE_MAX_DTE: 30,
|
|
86
|
+
|
|
87
|
+
BASE_NOTIONAL_USD: 5_000, // per trade (notional on ETF synth)
|
|
88
|
+
PROFIT_TRAIL_TRIGGER_BPS: 10, // start trailing at +10 bps
|
|
89
|
+
DEFAULT_STOP_BPS: 20, // hard stop at -20 bps when unhedged
|
|
90
|
+
|
|
91
|
+
POLL_MS: 30_000, // loop delay
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// ===== Alpaca setup =====
|
|
95
|
+
const alpaca = new Alpaca({
|
|
96
|
+
keyId: process.env.ALPACA_KEY_ID,
|
|
97
|
+
secretKey: process.env.ALPACA_SECRET_KEY,
|
|
98
|
+
paper: process.env.ALPACA_PAPER !== 'false',
|
|
99
|
+
rate_limit: true,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const ALPACA_REST_BASE =
|
|
103
|
+
process.env.ALPACA_PAPER === 'false'
|
|
104
|
+
? 'https://api.alpaca.markets'
|
|
105
|
+
: 'https://paper-api.alpaca.markets';
|
|
106
|
+
|
|
107
|
+
const AUTH_HEADERS = {
|
|
108
|
+
'APCA-API-KEY-ID': process.env.ALPACA_KEY_ID,
|
|
109
|
+
'APCA-API-SECRET-KEY': process.env.ALPACA_SECRET_KEY,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
function sleep(ms) {
|
|
113
|
+
return new Promise(res => setTimeout(res, ms));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ===== Data + SMA helpers (Alpaca ETF bars) =====
|
|
117
|
+
|
|
118
|
+
async function fetchRecentBars(symbol) {
|
|
119
|
+
const end = new Date().toISOString();
|
|
120
|
+
const start = new Date(
|
|
121
|
+
Date.now() - CFG.BAR_LOOKBACK_DAYS * 24 * 60 * 60 * 1000
|
|
122
|
+
).toISOString();
|
|
123
|
+
|
|
124
|
+
const bars = [];
|
|
125
|
+
const gen = alpaca.getBarsV2(symbol, {
|
|
126
|
+
start,
|
|
127
|
+
end,
|
|
128
|
+
timeframe: CFG.BAR_TIMEFRAME,
|
|
129
|
+
limit: CFG.BAR_LIMIT,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
for await (const b of gen) {
|
|
133
|
+
bars.push(b);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
bars.sort((a, b) => new Date(a.t) - new Date(b.t)); // ascending
|
|
137
|
+
return bars;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function smaForPeriod(bars, period, field = 'c') {
|
|
141
|
+
if (bars.length < period) return null;
|
|
142
|
+
let sum = 0;
|
|
143
|
+
for (let i = bars.length - period; i < bars.length; i++) {
|
|
144
|
+
sum += Number(bars[i][field]);
|
|
145
|
+
}
|
|
146
|
+
return sum / period;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function lastN(bars, n) {
|
|
150
|
+
if (bars.length <= n) return bars.slice();
|
|
151
|
+
return bars.slice(bars.length - n);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ===== Multi-fib trend + retrace detection =====
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Detect trend + retrace using a ladder of Fibonacci SMAs:
|
|
158
|
+
*
|
|
159
|
+
* 1. Compute SMA for all CFG.FIB_PERIODS.
|
|
160
|
+
* 2. For each triple [pShort, pMid, pLong] in CFG.FIB_TRIPLES (ascending):
|
|
161
|
+
* uptrend: SMA(pShort) > SMA(pMid) > SMA(pLong)
|
|
162
|
+
* downtrend: SMA(pShort) < SMA(pMid) < SMA(pLong)
|
|
163
|
+
* Keep the triple with the *largest pLong* that matches (longest “stack”).
|
|
164
|
+
* 3. Entry level is SMA(pShort) of that best triple (“next one” below the longer two).
|
|
165
|
+
* 4. Retrace condition:
|
|
166
|
+
* prev bar range crosses entry SMA
|
|
167
|
+
* last bar closes back in direction of trend.
|
|
168
|
+
*
|
|
169
|
+
* Returns:
|
|
170
|
+
* null OR
|
|
171
|
+
* { side: 'LONG' | 'SHORT', entryPeriod: number, triple: [pShort,pMid,pLong] }
|
|
172
|
+
*/
|
|
173
|
+
function detectFibSignal(bars) {
|
|
174
|
+
if (bars.length < 200) return null; // sanity; you can relax this
|
|
175
|
+
|
|
176
|
+
const latest = bars[bars.length - 1];
|
|
177
|
+
const prev = bars[bars.length - 2];
|
|
178
|
+
|
|
179
|
+
// 1) SMA map
|
|
180
|
+
const smaMap = {};
|
|
181
|
+
for (const p of CFG.FIB_PERIODS) {
|
|
182
|
+
smaMap[p] = smaForPeriod(bars, p);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// 2) find best triple (largest pLong)
|
|
186
|
+
let best = null;
|
|
187
|
+
|
|
188
|
+
for (const triple of CFG.FIB_TRIPLES) {
|
|
189
|
+
const [pShort, pMid, pLong] = triple;
|
|
190
|
+
const sShort = smaMap[pShort];
|
|
191
|
+
const sMid = smaMap[pMid];
|
|
192
|
+
const sLong = smaMap[pLong];
|
|
193
|
+
|
|
194
|
+
if (sShort == null || sMid == null || sLong == null) continue;
|
|
195
|
+
|
|
196
|
+
const upTrend = sShort > sMid && sMid > sLong;
|
|
197
|
+
const downTrend = sShort < sMid && sMid < sLong;
|
|
198
|
+
|
|
199
|
+
if (!upTrend && !downTrend) continue;
|
|
200
|
+
|
|
201
|
+
if (
|
|
202
|
+
!best ||
|
|
203
|
+
pLong > best.triple[2] // prefer triple with longest long-period
|
|
204
|
+
) {
|
|
205
|
+
best = {
|
|
206
|
+
side: upTrend ? 'LONG' : 'SHORT',
|
|
207
|
+
triple: triple,
|
|
208
|
+
entryPeriod: pShort, // “next one when the longer ones are above each other”
|
|
209
|
+
smaEntry: sShort,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (!best) return null;
|
|
215
|
+
|
|
216
|
+
// 3) retrace condition to entry SMA
|
|
217
|
+
const entrySma = best.smaEntry;
|
|
218
|
+
const prevTouched =
|
|
219
|
+
Number(prev.l) <= entrySma && Number(prev.h) >= entrySma;
|
|
220
|
+
|
|
221
|
+
if (!prevTouched) return null;
|
|
222
|
+
|
|
223
|
+
if (best.side === 'LONG') {
|
|
224
|
+
if (Number(latest.c) > entrySma) return best;
|
|
225
|
+
} else {
|
|
226
|
+
if (Number(latest.c) < entrySma) return best;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ===== Options hedge helpers (Alpaca) =====
|
|
233
|
+
|
|
234
|
+
async function pickClosestOption(symbol, type, underlyingPx) {
|
|
235
|
+
const url = `${ALPACA_REST_BASE}/v2/options/contracts`;
|
|
236
|
+
|
|
237
|
+
const res = await axios.get(url, {
|
|
238
|
+
headers: AUTH_HEADERS,
|
|
239
|
+
params: {
|
|
240
|
+
underlying_symbols: symbol,
|
|
241
|
+
type, // 'call' | 'put'
|
|
242
|
+
limit: 500,
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const contracts = (res.data.option_contracts || []).filter(c => c.tradable);
|
|
247
|
+
if (!contracts.length) return null;
|
|
248
|
+
|
|
249
|
+
const today = new Date();
|
|
250
|
+
|
|
251
|
+
function dte(expStr) {
|
|
252
|
+
const d = new Date(expStr + 'T16:00:00Z');
|
|
253
|
+
return (d - today) / (24 * 60 * 60 * 1000);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const filtered = contracts.filter(c => {
|
|
257
|
+
const d = dte(c.expiration_date);
|
|
258
|
+
return d >= CFG.HEDGE_MIN_DTE && d <= CFG.HEDGE_MAX_DTE;
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
const pool = filtered.length ? filtered : contracts;
|
|
262
|
+
|
|
263
|
+
let best = null;
|
|
264
|
+
let bestDiff = Infinity;
|
|
265
|
+
for (const c of pool) {
|
|
266
|
+
const strike = Number(c.strike_price);
|
|
267
|
+
const diff = Math.abs(strike - underlyingPx);
|
|
268
|
+
if (diff < bestDiff) {
|
|
269
|
+
bestDiff = diff;
|
|
270
|
+
best = c;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return best || null;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ===== TL order helpers (TODO: wire these to your ApiWrapper methods) =====
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Place a TL market order via ApiWrapper.
|
|
280
|
+
*
|
|
281
|
+
* You *must* adapt this to your actual API. For example, if you have:
|
|
282
|
+
* api.spotMarketOrder({ market, side, amount })
|
|
283
|
+
* or
|
|
284
|
+
* api.placeOrder({ market, side, type: 'MARKET', size })
|
|
285
|
+
* then use that here.
|
|
286
|
+
*/
|
|
287
|
+
async function tlMarketOrder(side, qty) {
|
|
288
|
+
console.log(`[TL] MARKET ${side} ${qty} ${CFG.TL_MARKET}`);
|
|
289
|
+
|
|
290
|
+
// TODO: replace this with your real call.
|
|
291
|
+
// Example placeholder:
|
|
292
|
+
if (typeof tlApi.placeOrder === 'function') {
|
|
293
|
+
return tlApi.placeOrder({
|
|
294
|
+
market: CFG.TL_MARKET,
|
|
295
|
+
side, // 'buy' | 'sell'
|
|
296
|
+
type: 'MARKET',
|
|
297
|
+
size: qty,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// If your wrapper uses a different signature, adjust accordingly:
|
|
302
|
+
// return tlApi.spotMarketOrder(CFG.TL_MARKET, side, qty);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// ===== Hedge management =====
|
|
306
|
+
|
|
307
|
+
async function closeHedgeIfAny() {
|
|
308
|
+
if (!currentHedgeSymbol) return;
|
|
309
|
+
|
|
310
|
+
try {
|
|
311
|
+
const positions = await alpaca.getPositions();
|
|
312
|
+
const hedgePos = positions.find(
|
|
313
|
+
p => p.symbol === currentHedgeSymbol && p.asset_class === 'option'
|
|
314
|
+
);
|
|
315
|
+
if (!hedgePos) {
|
|
316
|
+
currentHedgeSymbol = null;
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const qty = Number(hedgePos.qty);
|
|
321
|
+
if (!qty) {
|
|
322
|
+
currentHedgeSymbol = null;
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const side = hedgePos.side === 'long' ? 'sell' : 'buy';
|
|
327
|
+
console.log(`[EXIT HEDGE] ${currentHedgeSymbol} ${side} ${qty}`);
|
|
328
|
+
|
|
329
|
+
await alpaca.createOrder({
|
|
330
|
+
symbol: currentHedgeSymbol,
|
|
331
|
+
qty,
|
|
332
|
+
side,
|
|
333
|
+
type: 'market',
|
|
334
|
+
time_in_force: 'day',
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
currentHedgeSymbol = null;
|
|
338
|
+
} catch (e) {
|
|
339
|
+
console.log('Error closing hedge:', e.message || e);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// ===== Position & risk helpers =====
|
|
344
|
+
|
|
345
|
+
function bpsFrom(entry, current, side) {
|
|
346
|
+
if (side === 'long') {
|
|
347
|
+
return ((current - entry) / entry) * 10_000;
|
|
348
|
+
}
|
|
349
|
+
// short: profit when price goes down
|
|
350
|
+
return ((entry - current) / entry) * 10_000;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async function openTlAndMaybeHedge(signal, lastPrice) {
|
|
354
|
+
if (currentPosition) {
|
|
355
|
+
console.log('[SKIP] Already in TL position.');
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const shares = Math.max(1, Math.floor(CFG.BASE_NOTIONAL_USD / lastPrice));
|
|
360
|
+
const side = signal.side === 'LONG' ? 'buy' : 'sell';
|
|
361
|
+
|
|
362
|
+
console.log(
|
|
363
|
+
`[ENTRY] ${signal.side} TL ${CFG.TL_MARKET} ${shares} @ ETF≈${lastPrice.toFixed(
|
|
364
|
+
2
|
|
365
|
+
)} using triple ${signal.triple.join('/')}`
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
await tlMarketOrder(side, shares);
|
|
369
|
+
|
|
370
|
+
currentPosition = {
|
|
371
|
+
side: signal.side === 'LONG' ? 'long' : 'short',
|
|
372
|
+
qty: shares,
|
|
373
|
+
entryPrice: lastPrice,
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
if (!CFG.USE_OPTION_HEDGE) {
|
|
377
|
+
console.log('[HEDGE] Disabled (USE_OPTION_HEDGE = false)');
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
try {
|
|
382
|
+
const hedgeType = signal.side === 'LONG' ? 'put' : 'call';
|
|
383
|
+
const contract = await pickClosestOption(CFG.ETF_SYMBOL, hedgeType, lastPrice);
|
|
384
|
+
if (!contract) {
|
|
385
|
+
console.log('[HEDGE] No suitable option contract found.');
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const optSize = Number(contract.size || '100'); // contract multiplier
|
|
390
|
+
const optQty = Math.max(1, Math.round(shares / optSize));
|
|
391
|
+
|
|
392
|
+
console.log(
|
|
393
|
+
`[HEDGE] Buying ${optQty} ${contract.symbol} (${hedgeType}) near ${contract.strike_price}`
|
|
394
|
+
);
|
|
395
|
+
|
|
396
|
+
await alpaca.createOrder({
|
|
397
|
+
symbol: contract.symbol,
|
|
398
|
+
qty: optQty,
|
|
399
|
+
side: 'buy',
|
|
400
|
+
type: 'market',
|
|
401
|
+
time_in_force: 'day',
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
currentHedgeSymbol = contract.symbol;
|
|
405
|
+
} catch (e) {
|
|
406
|
+
console.log('Error creating hedge order:', e.message || e);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
async function closeTlAndHedge() {
|
|
411
|
+
if (!currentPosition) return;
|
|
412
|
+
|
|
413
|
+
const exitSide = currentPosition.side === 'long' ? 'sell' : 'buy';
|
|
414
|
+
|
|
415
|
+
console.log(
|
|
416
|
+
`[EXIT] TL ${CFG.TL_MARKET} ${exitSide} ${currentPosition.qty}`
|
|
417
|
+
);
|
|
418
|
+
await tlMarketOrder(exitSide, currentPosition.qty);
|
|
419
|
+
|
|
420
|
+
await closeHedgeIfAny();
|
|
421
|
+
currentPosition = null;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// ===== Stop logic (last 5 bars + hard stop) =====
|
|
425
|
+
|
|
426
|
+
async function manageStops(bars) {
|
|
427
|
+
if (!currentPosition) return;
|
|
428
|
+
|
|
429
|
+
const last = bars[bars.length - 1];
|
|
430
|
+
const lastPrice = Number(last.c);
|
|
431
|
+
const entry = currentPosition.entryPrice;
|
|
432
|
+
const pnlBps = bpsFrom(entry, lastPrice, currentPosition.side);
|
|
433
|
+
|
|
434
|
+
// Hard stop when unhedged
|
|
435
|
+
if (!CFG.USE_OPTION_HEDGE && pnlBps <= -CFG.DEFAULT_STOP_BPS) {
|
|
436
|
+
console.log(
|
|
437
|
+
`[STOP] Hard stop hit at ${pnlBps.toFixed(
|
|
438
|
+
2
|
|
439
|
+
)} bps (no hedge). Closing TL position.`
|
|
440
|
+
);
|
|
441
|
+
await closeTlAndHedge();
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Trailing stop once sufficiently in profit
|
|
446
|
+
if (pnlBps >= CFG.PROFIT_TRAIL_TRIGGER_BPS) {
|
|
447
|
+
const last5 = lastN(bars, 5);
|
|
448
|
+
const lows = last5.map(b => Number(b.l));
|
|
449
|
+
const highs = last5.map(b => Number(b.h));
|
|
450
|
+
|
|
451
|
+
const trailLevelLong = Math.min(...lows);
|
|
452
|
+
const trailLevelShort = Math.max(...highs);
|
|
453
|
+
|
|
454
|
+
if (
|
|
455
|
+
currentPosition.side === 'long' &&
|
|
456
|
+
lastPrice <= trailLevelLong
|
|
457
|
+
) {
|
|
458
|
+
console.log(
|
|
459
|
+
`[TRAIL] Long stop triggered @ ${lastPrice} <= ${trailLevelLong}`
|
|
460
|
+
);
|
|
461
|
+
await closeTlAndHedge();
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (
|
|
466
|
+
currentPosition.side === 'short' &&
|
|
467
|
+
lastPrice >= trailLevelShort
|
|
468
|
+
) {
|
|
469
|
+
console.log(
|
|
470
|
+
`[TRAIL] Short stop triggered @ ${lastPrice} >= ${trailLevelShort}`
|
|
471
|
+
);
|
|
472
|
+
await closeTlAndHedge();
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// ===== Main loop =====
|
|
479
|
+
|
|
480
|
+
async function mainLoop() {
|
|
481
|
+
console.log(
|
|
482
|
+
`Starting fib_sma_taker_tl for TL market=${CFG.TL_MARKET}, ETF=${CFG.ETF_SYMBOL}, timeframe=${CFG.BAR_TIMEFRAME}, hedge=${CFG.USE_OPTION_HEDGE ? 'ON' : 'OFF'}`
|
|
483
|
+
);
|
|
484
|
+
|
|
485
|
+
while (true) {
|
|
486
|
+
try {
|
|
487
|
+
const bars = await fetchRecentBars(CFG.ETF_SYMBOL);
|
|
488
|
+
if (!bars.length) {
|
|
489
|
+
console.log('[WARN] No bars returned, skipping loop.');
|
|
490
|
+
await sleep(CFG.POLL_MS);
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const last = bars[bars.length - 1];
|
|
495
|
+
const lastPx = Number(last.c);
|
|
496
|
+
|
|
497
|
+
// 1) manage open TL position
|
|
498
|
+
await manageStops(bars);
|
|
499
|
+
|
|
500
|
+
// 2) consider new entry if flat
|
|
501
|
+
if (!currentPosition) {
|
|
502
|
+
const sig = detectFibSignal(bars);
|
|
503
|
+
if (sig) {
|
|
504
|
+
console.log(
|
|
505
|
+
`[SIGNAL] ${sig.side} @ close=${lastPx.toFixed(
|
|
506
|
+
4
|
|
507
|
+
)} using triple ${sig.triple.join('/')}, entry SMA=${sig.entryPeriod}`
|
|
508
|
+
);
|
|
509
|
+
await openTlAndMaybeHedge(sig, lastPx);
|
|
510
|
+
} else {
|
|
511
|
+
console.log('[NO SIGNAL] Waiting…');
|
|
512
|
+
}
|
|
513
|
+
} else {
|
|
514
|
+
console.log(
|
|
515
|
+
`[IN POSITION] side=${currentPosition.side} qty=${currentPosition.qty} entry=${currentPosition.entryPrice.toFixed(
|
|
516
|
+
4
|
|
517
|
+
)} etf_last=${lastPx.toFixed(4)}`
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
} catch (e) {
|
|
521
|
+
console.error('Loop error:', e.message || e);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
await sleep(CFG.POLL_MS);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
mainLoop().catch(e => {
|
|
529
|
+
console.error('Fatal error:', e.message || e);
|
|
530
|
+
process.exit(1);
|
|
531
|
+
});
|
package/decodeTest.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const { Psbt } = require('bitcoinjs-lib');
|
|
2
|
+
const { ECPair } = require('bitcoinjs-lib');
|
|
3
|
+
const { payments } = require('bitcoinjs-lib');
|
|
4
|
+
const { Buffer } = require('buffer');
|
|
5
|
+
|
|
6
|
+
// Example PSBT object (this should be replaced with your actual PSBT data)
|
|
7
|
+
const psbtHex = "70736274ff0100c50200000002343025a90f60c20c72637d18c3dcf46dd31b7e1abe409982c1daa3f1753f6b060000000000ffffffffd7a4390df4a8811be886b1251343af95fe927567e3cb450ec42d797076e2b7960000000000ffffffff03b4db060000000000160014f94801072fc65f8c35db2363f75803f16a9da94b1027000000000000160014396d64761fb3feb3fcaf95bf1c21c8f36d680a7e0000000000000000226a20746c33302c302e3030346e796d386571756170712c302c3770732c302c312c30000000000001012b5415000000000000220020a30d39561520a26d39248fb24062bb1997df52e8b73a22828534e9d58ecd58ff220203f9967f334d7bcbe7bcefa41252ba488c3ceadf6da6c269bcada1ed0b9d600a5047304402205d097e72787447472f8b3edee44067c7ac313421725eb2b884fe01d7e6a3352a02200592f4809141b1c1fccaf900c351797ba8f8d7425298257967509e7f6668afc201010547522103f9967f334d7bcbe7bcefa41252ba488c3ceadf6da6c269bcada1ed0b9d600a5021035d0c4cf2ea856bef98dba896f7e82bd9d80cedd08d117253e9ce225079f72b4b52ae0001011f90f0060000000000160014f94801072fc65f8c35db2363f75803f16a9da94b00000000"
|
|
8
|
+
// Decode the PSBT
|
|
9
|
+
const psbt = Psbt.fromHex(psbtHex);
|
|
10
|
+
|
|
11
|
+
// Check all inputs and their validity
|
|
12
|
+
psbt.data.inputs.forEach((input, index) => {
|
|
13
|
+
console.log(`Analyzing input ${index + 1} - TXID: ${input.txid}`);
|
|
14
|
+
|
|
15
|
+
// Check witness script
|
|
16
|
+
if (input.witnessUtxo && input.witnessUtxo.script) {
|
|
17
|
+
const witnessScript = input.witnessUtxo.script;
|
|
18
|
+
console.log(`Witness script for input ${index + 1}: `, witnessScript.toString('hex'));
|
|
19
|
+
|
|
20
|
+
// If it's multisig, it should match the expected structure
|
|
21
|
+
if (input.witnessScript) {
|
|
22
|
+
const isMultisig = witnessScript[0] === 0x52; // 0x52 is the OP_CHECKMULTISIG opcode
|
|
23
|
+
if (isMultisig) {
|
|
24
|
+
console.log("This input is a multisig input.");
|
|
25
|
+
// Decode and check public keys in multisig (optional)
|
|
26
|
+
} else {
|
|
27
|
+
console.log("This input is not a multisig input.");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Verify the signatures
|
|
33
|
+
if (input.partialSig) {
|
|
34
|
+
input.partialSig.forEach((sigData, sigIndex) => {
|
|
35
|
+
const pubkey = sigData.pubkey.toString('hex');
|
|
36
|
+
const signature = sigData.signature.toString('hex');
|
|
37
|
+
console.log(`Signature ${sigIndex + 1} for input ${index + 1}:`);
|
|
38
|
+
console.log(`Pubkey: ${pubkey}`);
|
|
39
|
+
console.log(`Signature: ${signature}`);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Check all outputs
|
|
45
|
+
psbt.data.outputs.forEach((output, index) => {
|
|
46
|
+
console.log(`Analyzing output ${index + 1} - Value: ${output.value}`);
|
|
47
|
+
|
|
48
|
+
const script = output.script;
|
|
49
|
+
console.log(`Output scriptPubKey for output ${index + 1}: `, script.toString('hex'));
|
|
50
|
+
|
|
51
|
+
// Check if it's a P2WPKH or P2PKH output
|
|
52
|
+
const p2wpkh = payments.p2wpkh({ output: script });
|
|
53
|
+
const p2pkh = payments.p2pkh({ output: script });
|
|
54
|
+
|
|
55
|
+
if (p2wpkh) {
|
|
56
|
+
console.log("This is a P2WPKH output.");
|
|
57
|
+
} else if (p2pkh) {
|
|
58
|
+
console.log("This is a P2PKH output.");
|
|
59
|
+
} else {
|
|
60
|
+
console.log("This is an unknown output type.");
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Check that the PSBT is finalized (signed)
|
|
65
|
+
if (psbt.finalized) {
|
|
66
|
+
console.log("PSBT is finalized (signed).");
|
|
67
|
+
} else {
|
|
68
|
+
console.log("PSBT is not finalized. It may still need signing.");
|
|
69
|
+
}
|