Compare commits
29
Commits
old-approch
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef4cb47d06 | ||
|
|
5657b087c5 | ||
|
|
d7e2f8d04f | ||
|
|
02bb3eefee | ||
|
|
e88752cb23 | ||
|
|
fa31c52cc3 | ||
|
|
c1150f927a | ||
|
|
c97f96765c | ||
|
|
3eeea819a9 | ||
|
|
768ec1358c | ||
|
|
bbfb33829e | ||
|
|
7508654d00 | ||
|
|
5a579d49c0 | ||
|
|
da9244eabd | ||
|
|
7abcd3642c | ||
|
|
25d7e2a2d4 | ||
|
|
fd0e2b31b1 | ||
|
|
ca8e167275 | ||
|
|
351a76a308 | ||
|
|
b4c3eb90c9 | ||
|
|
430f2e9ad0 | ||
|
|
32518bfceb | ||
|
|
e0cd82553a | ||
|
|
1de0ebd22e | ||
|
|
bf1c3515c0 | ||
|
|
a67afb031c | ||
|
|
145fc5e6f4 | ||
|
|
a31995f362 | ||
|
|
98fe566336 |
+721
@@ -0,0 +1,721 @@
|
|||||||
|
ctf_abi = [
|
||||||
|
{
|
||||||
|
"constant":True,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"name":"owner",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"id",
|
||||||
|
"type":"uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"balanceOf",
|
||||||
|
"outputs":[
|
||||||
|
{
|
||||||
|
"name":"",
|
||||||
|
"type":"uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable":False,
|
||||||
|
"stateMutability":"view",
|
||||||
|
"type":"function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant":True,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"name":"interfaceId",
|
||||||
|
"type":"bytes4"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"supportsInterface",
|
||||||
|
"outputs":[
|
||||||
|
{
|
||||||
|
"name":"",
|
||||||
|
"type":"bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable":False,
|
||||||
|
"stateMutability":"view",
|
||||||
|
"type":"function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant":True,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"name":"",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"",
|
||||||
|
"type":"uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"payoutNumerators",
|
||||||
|
"outputs":[
|
||||||
|
{
|
||||||
|
"name":"",
|
||||||
|
"type":"uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable":False,
|
||||||
|
"stateMutability":"view",
|
||||||
|
"type":"function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant":False,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"name":"from",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"to",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"ids",
|
||||||
|
"type":"uint256[]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"values",
|
||||||
|
"type":"uint256[]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"data",
|
||||||
|
"type":"bytes"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"safeBatchTransferFrom",
|
||||||
|
"outputs":[
|
||||||
|
|
||||||
|
],
|
||||||
|
"payable":False,
|
||||||
|
"stateMutability":"nonpayable",
|
||||||
|
"type":"function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant":True,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"name":"owners",
|
||||||
|
"type":"address[]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"ids",
|
||||||
|
"type":"uint256[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"balanceOfBatch",
|
||||||
|
"outputs":[
|
||||||
|
{
|
||||||
|
"name":"",
|
||||||
|
"type":"uint256[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable":False,
|
||||||
|
"stateMutability":"view",
|
||||||
|
"type":"function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant":False,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"name":"operator",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"approved",
|
||||||
|
"type":"bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"setApprovalForAll",
|
||||||
|
"outputs":[
|
||||||
|
|
||||||
|
],
|
||||||
|
"payable":False,
|
||||||
|
"stateMutability":"nonpayable",
|
||||||
|
"type":"function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant":True,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"name":"",
|
||||||
|
"type":"bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"payoutDenominator",
|
||||||
|
"outputs":[
|
||||||
|
{
|
||||||
|
"name":"",
|
||||||
|
"type":"uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable":False,
|
||||||
|
"stateMutability":"view",
|
||||||
|
"type":"function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant":True,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"name":"owner",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"operator",
|
||||||
|
"type":"address"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"isApprovedForAll",
|
||||||
|
"outputs":[
|
||||||
|
{
|
||||||
|
"name":"",
|
||||||
|
"type":"bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable":False,
|
||||||
|
"stateMutability":"view",
|
||||||
|
"type":"function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant":False,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"name":"from",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"to",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"id",
|
||||||
|
"type":"uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"value",
|
||||||
|
"type":"uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"data",
|
||||||
|
"type":"bytes"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"safeTransferFrom",
|
||||||
|
"outputs":[
|
||||||
|
|
||||||
|
],
|
||||||
|
"payable":False,
|
||||||
|
"stateMutability":"nonpayable",
|
||||||
|
"type":"function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous":False,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"conditionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"oracle",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"questionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":False,
|
||||||
|
"name":"outcomeSlotCount",
|
||||||
|
"type":"uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"ConditionPreparation",
|
||||||
|
"type":"event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous":False,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"conditionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"oracle",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"questionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":False,
|
||||||
|
"name":"outcomeSlotCount",
|
||||||
|
"type":"uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":False,
|
||||||
|
"name":"payoutNumerators",
|
||||||
|
"type":"uint256[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"ConditionResolution",
|
||||||
|
"type":"event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous":False,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"stakeholder",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":False,
|
||||||
|
"name":"collateralToken",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"parentCollectionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"conditionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":False,
|
||||||
|
"name":"partition",
|
||||||
|
"type":"uint256[]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":False,
|
||||||
|
"name":"amount",
|
||||||
|
"type":"uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"PositionSplit",
|
||||||
|
"type":"event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous":False,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"stakeholder",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":False,
|
||||||
|
"name":"collateralToken",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"parentCollectionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"conditionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":False,
|
||||||
|
"name":"partition",
|
||||||
|
"type":"uint256[]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":False,
|
||||||
|
"name":"amount",
|
||||||
|
"type":"uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"PositionsMerge",
|
||||||
|
"type":"event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous":False,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"redeemer",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"collateralToken",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"parentCollectionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":False,
|
||||||
|
"name":"conditionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":False,
|
||||||
|
"name":"indexSets",
|
||||||
|
"type":"uint256[]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":False,
|
||||||
|
"name":"payout",
|
||||||
|
"type":"uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"PayoutRedemption",
|
||||||
|
"type":"event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous":False,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"operator",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"from",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"to",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":False,
|
||||||
|
"name":"id",
|
||||||
|
"type":"uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":False,
|
||||||
|
"name":"value",
|
||||||
|
"type":"uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"TransferSingle",
|
||||||
|
"type":"event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous":False,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"operator",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"from",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"to",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":False,
|
||||||
|
"name":"ids",
|
||||||
|
"type":"uint256[]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":False,
|
||||||
|
"name":"values",
|
||||||
|
"type":"uint256[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"TransferBatch",
|
||||||
|
"type":"event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous":False,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"owner",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"operator",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":False,
|
||||||
|
"name":"approved",
|
||||||
|
"type":"bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"ApprovalForAll",
|
||||||
|
"type":"event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous":False,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"indexed":False,
|
||||||
|
"name":"value",
|
||||||
|
"type":"string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed":True,
|
||||||
|
"name":"id",
|
||||||
|
"type":"uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"URI",
|
||||||
|
"type":"event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant":False,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"name":"oracle",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"questionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"outcomeSlotCount",
|
||||||
|
"type":"uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"prepareCondition",
|
||||||
|
"outputs":[
|
||||||
|
|
||||||
|
],
|
||||||
|
"payable":False,
|
||||||
|
"stateMutability":"nonpayable",
|
||||||
|
"type":"function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant":False,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"name":"questionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"payouts",
|
||||||
|
"type":"uint256[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"reportPayouts",
|
||||||
|
"outputs":[
|
||||||
|
|
||||||
|
],
|
||||||
|
"payable":False,
|
||||||
|
"stateMutability":"nonpayable",
|
||||||
|
"type":"function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant":False,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"name":"collateralToken",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"parentCollectionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"conditionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"partition",
|
||||||
|
"type":"uint256[]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"amount",
|
||||||
|
"type":"uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"splitPosition",
|
||||||
|
"outputs":[
|
||||||
|
|
||||||
|
],
|
||||||
|
"payable":False,
|
||||||
|
"stateMutability":"nonpayable",
|
||||||
|
"type":"function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant":False,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"name":"collateralToken",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"parentCollectionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"conditionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"partition",
|
||||||
|
"type":"uint256[]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"amount",
|
||||||
|
"type":"uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"mergePositions",
|
||||||
|
"outputs":[
|
||||||
|
|
||||||
|
],
|
||||||
|
"payable":False,
|
||||||
|
"stateMutability":"nonpayable",
|
||||||
|
"type":"function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant":False,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"name":"collateralToken",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"parentCollectionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"conditionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"indexSets",
|
||||||
|
"type":"uint256[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"redeemPositions",
|
||||||
|
"outputs":[
|
||||||
|
|
||||||
|
],
|
||||||
|
"payable":False,
|
||||||
|
"stateMutability":"nonpayable",
|
||||||
|
"type":"function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant":True,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"name":"conditionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"getOutcomeSlotCount",
|
||||||
|
"outputs":[
|
||||||
|
{
|
||||||
|
"name":"",
|
||||||
|
"type":"uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable":False,
|
||||||
|
"stateMutability":"view",
|
||||||
|
"type":"function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant":True,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"name":"oracle",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"questionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"outcomeSlotCount",
|
||||||
|
"type":"uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"getConditionId",
|
||||||
|
"outputs":[
|
||||||
|
{
|
||||||
|
"name":"",
|
||||||
|
"type":"bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable":False,
|
||||||
|
"stateMutability":"pure",
|
||||||
|
"type":"function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant":True,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"name":"parentCollectionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"conditionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"indexSet",
|
||||||
|
"type":"uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"getCollectionId",
|
||||||
|
"outputs":[
|
||||||
|
{
|
||||||
|
"name":"",
|
||||||
|
"type":"bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable":False,
|
||||||
|
"stateMutability":"view",
|
||||||
|
"type":"function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant":True,
|
||||||
|
"inputs":[
|
||||||
|
{
|
||||||
|
"name":"collateralToken",
|
||||||
|
"type":"address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"collectionId",
|
||||||
|
"type":"bytes32"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name":"getPositionId",
|
||||||
|
"outputs":[
|
||||||
|
{
|
||||||
|
"name":"",
|
||||||
|
"type":"uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable":False,
|
||||||
|
"stateMutability":"pure",
|
||||||
|
"type":"function"
|
||||||
|
}
|
||||||
|
]
|
||||||
+1138
File diff suppressed because it is too large
Load Diff
@@ -5,9 +5,12 @@ POLYMARKET_WS_MARKET_URL = "wss://ws-subscriptions-clob.polymarket.com/ws/market
|
|||||||
POLYMARKET_WS_USER_URL = "wss://ws-subscriptions-clob.polymarket.com/ws/user"
|
POLYMARKET_WS_USER_URL = "wss://ws-subscriptions-clob.polymarket.com/ws/user"
|
||||||
CHAIN_ID = 137
|
CHAIN_ID = 137
|
||||||
REQUEST_TIMEOUT = 5
|
REQUEST_TIMEOUT = 5
|
||||||
PROFIT_MARGIN = 0.02
|
PROFIT_MARGIN = 0.04
|
||||||
TRADING_BPS_THRESHOLD = 50
|
TRADING_BPS_THRESHOLD = 50
|
||||||
|
MAX_TRADING_BPS_THRESHOLD = 100
|
||||||
MARKET_SESSION_SECONDS = 900
|
MARKET_SESSION_SECONDS = 900
|
||||||
TIMEZONE = "US/Eastern"
|
TIMEZONE = "US/Eastern"
|
||||||
MAX_TRADES = 2
|
MAX_TRADES = 30
|
||||||
|
MAX_INVENTORY = 1
|
||||||
|
MIN_DELAY_BETWEEN_TRADES_SECONDS = 1
|
||||||
PLACE_OPPOSITE_ORDER = True # Hedge orders
|
PLACE_OPPOSITE_ORDER = True # Hedge orders
|
||||||
|
|||||||
@@ -1,182 +0,0 @@
|
|||||||
use std::collections::HashSet;
|
|
||||||
use std::io::{Read, Write};
|
|
||||||
use std::net::{TcpListener, TcpStream};
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use std::thread;
|
|
||||||
|
|
||||||
struct InMemSetDB {
|
|
||||||
data: Arc<Mutex<HashSet<String>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InMemSetDB {
|
|
||||||
fn new() -> Self {
|
|
||||||
InMemSetDB {
|
|
||||||
data: Arc::new(Mutex::new(HashSet::new())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add(&self, item: String) -> String {
|
|
||||||
let mut set = self.data.lock().unwrap();
|
|
||||||
set.insert(item);
|
|
||||||
r#"{"success":true}"#.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn contains(&self, item: &str) -> String {
|
|
||||||
let set = self.data.lock().unwrap();
|
|
||||||
let exists = set.contains(item);
|
|
||||||
format!(r#"{{"exists":{}}}"#, exists)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear(&self) -> String {
|
|
||||||
let mut set = self.data.lock().unwrap();
|
|
||||||
set.clear();
|
|
||||||
r#"{"success":true}"#.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size(&self) -> String {
|
|
||||||
let set = self.data.lock().unwrap();
|
|
||||||
format!(r#"{{"size":{}}}"#, set.len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_request(request: &str) -> Option<(String, String, Option<String>)> {
|
|
||||||
let lines: Vec<&str> = request.split("\r\n").collect();
|
|
||||||
if lines.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let parts: Vec<&str> = lines[0].split_whitespace().collect();
|
|
||||||
if parts.len() < 2 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let method = parts[0].to_string();
|
|
||||||
let path = parts[1].to_string();
|
|
||||||
|
|
||||||
let body = if method == "POST" {
|
|
||||||
if let Some(idx) = request.find("\r\n\r\n") {
|
|
||||||
Some(request[idx + 4..].to_string())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
Some((method, path, body))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extract_json_field(json: &str, field: &str) -> Option<String> {
|
|
||||||
let search = format!(r#""{}":""#, field);
|
|
||||||
if let Some(start) = json.find(&search) {
|
|
||||||
let value_start = start + search.len();
|
|
||||||
if let Some(end) = json[value_start..].find('"') {
|
|
||||||
return Some(json[value_start..value_start + end].to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_request(
|
|
||||||
set: &InMemSetDB,
|
|
||||||
method: &str,
|
|
||||||
path: &str,
|
|
||||||
body: Option<String>,
|
|
||||||
) -> (u16, String) {
|
|
||||||
match (method, path) {
|
|
||||||
("POST", "/add") => {
|
|
||||||
if let Some(body_str) = body {
|
|
||||||
if let Some(item) = extract_json_field(&body_str, "item") {
|
|
||||||
let response = set.add(item);
|
|
||||||
return (200, response);
|
|
||||||
}
|
|
||||||
return (400, r#"{"error":"Missing item"}"#.to_string());
|
|
||||||
}
|
|
||||||
(400, r#"{"error":"Missing body"}"#.to_string())
|
|
||||||
}
|
|
||||||
("POST", "/contains") => {
|
|
||||||
if let Some(body_str) = body {
|
|
||||||
if let Some(item) = extract_json_field(&body_str, "item") {
|
|
||||||
let response = set.contains(&item);
|
|
||||||
return (200, response);
|
|
||||||
}
|
|
||||||
return (400, r#"{"error":"Missing item"}"#.to_string());
|
|
||||||
}
|
|
||||||
(400, r#"{"error":"Missing body"}"#.to_string())
|
|
||||||
}
|
|
||||||
("POST", "/clear") => {
|
|
||||||
let response = set.clear();
|
|
||||||
(200, response)
|
|
||||||
}
|
|
||||||
("GET", "/size") => {
|
|
||||||
let response = set.size();
|
|
||||||
(200, response)
|
|
||||||
}
|
|
||||||
_ => (404, r#"{"error":"Not found"}"#.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_response(status: u16, body: String) -> String {
|
|
||||||
let status_text = match status {
|
|
||||||
200 => "OK",
|
|
||||||
400 => "Bad Request",
|
|
||||||
404 => "Not Found",
|
|
||||||
_ => "Unknown",
|
|
||||||
};
|
|
||||||
|
|
||||||
format!(
|
|
||||||
"HTTP/1.1 {} {}\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
|
|
||||||
status, status_text, body.len(), body
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_client(mut stream: TcpStream, set: InMemSetDB) {
|
|
||||||
let mut buffer = [0; 4096];
|
|
||||||
|
|
||||||
match stream.read(&mut buffer) {
|
|
||||||
Ok(size) => {
|
|
||||||
if size == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let request = String::from_utf8_lossy(&buffer[..size]);
|
|
||||||
|
|
||||||
if let Some((method, path, body)) = parse_request(&request) {
|
|
||||||
let (status, response_body) = handle_request(&set, &method, &path, body);
|
|
||||||
let response = build_response(status, response_body);
|
|
||||||
|
|
||||||
let _ = stream.write_all(response.as_bytes());
|
|
||||||
let _ = stream.flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => eprintln!("Error reading from stream: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let listener = TcpListener::bind("127.0.0.1:8080").expect("Failed to bind");
|
|
||||||
let set = InMemSetDB::new();
|
|
||||||
|
|
||||||
println!("InMemSetDB started on http://127.0.0.1:8080");
|
|
||||||
println!("\nEndpoints:");
|
|
||||||
println!(r#" POST /add - {{"item": "value"}}"#);
|
|
||||||
println!(r#" POST /contains - {{"item": "value"}}"#);
|
|
||||||
println!(" POST /clear");
|
|
||||||
println!(" GET /size");
|
|
||||||
println!("\nWaiting for connections...\n");
|
|
||||||
|
|
||||||
for stream in listener.incoming() {
|
|
||||||
match stream {
|
|
||||||
Ok(stream) => {
|
|
||||||
let set_clone = InMemSetDB {
|
|
||||||
data: Arc::clone(&set.data),
|
|
||||||
};
|
|
||||||
|
|
||||||
thread::spawn(move || {
|
|
||||||
handle_client(stream, set_clone);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Err(e) => eprintln!("Connection failed: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
import socket
|
|
||||||
import time
|
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
def fast_http_request(method, path, body=None):
|
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
sock.connect(("127.0.0.1", 8080))
|
|
||||||
|
|
||||||
if body:
|
|
||||||
body_str = body
|
|
||||||
request = f"{method} {path} HTTP/1.1\r\nHost: localhost\r\nContent-Length: {len(body_str)}\r\n\r\n{body_str}"
|
|
||||||
else:
|
|
||||||
request = f"{method} {path} HTTP/1.1\r\nHost: localhost\r\n\r\n"
|
|
||||||
|
|
||||||
sock.send(request.encode())
|
|
||||||
response = sock.recv(4096).decode()
|
|
||||||
sock.close()
|
|
||||||
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def get_body_from_response(response):
|
|
||||||
return json.loads(response.split("\r\n\r\n", 1)[1])
|
|
||||||
|
|
||||||
|
|
||||||
def add_item(item):
|
|
||||||
body = f'{{"item":"{item}"}}'
|
|
||||||
response = fast_http_request("POST", "/add", body)
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def clear_items():
|
|
||||||
response = fast_http_request("POST", "/clear")
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def contains_item(item):
|
|
||||||
body = f'{{"item":"{item}"}}'
|
|
||||||
response = fast_http_request("POST", "/contains", body)
|
|
||||||
body = get_body_from_response(response)
|
|
||||||
return body["exists"]
|
|
||||||
|
|
||||||
|
|
||||||
def size():
|
|
||||||
response = fast_http_request("GET", "/size")
|
|
||||||
body = get_body_from_response(response)
|
|
||||||
return int(body["size"])
|
|
||||||
@@ -1,48 +1,44 @@
|
|||||||
import os
|
import os
|
||||||
import gc
|
import gc
|
||||||
import asyncio
|
import time
|
||||||
import requests
|
|
||||||
from utils.logger import setup_logging
|
from utils.logger import setup_logging
|
||||||
from utils.tokens import fetch_tokens
|
from utils.tokens import fetch_tokens
|
||||||
from utils.orderbook import OrderBook, SIGNALES
|
from utils.orderbook import OrderBook, SIGNALES
|
||||||
from utils.clob_client import init_global_client, is_client_ready
|
from utils.clob_client import init_global_client, is_client_ready
|
||||||
from utils.market_time import is_in_trading_window
|
from utils.market_time import is_in_trading_window, get_period_elapsed_seconds
|
||||||
from utils.trade_counter import reset_trades, get_trades_count, increment_trades
|
from utils.trade_counter import reset_trades, get_trades_count, increment_trades
|
||||||
from utils.clob_orders import (
|
from utils.clob_orders import (
|
||||||
place_anchor_and_hedge,
|
place_anchor_and_hedge,
|
||||||
cache_token_trading_infos,
|
cache_token_trading_infos,
|
||||||
)
|
)
|
||||||
from utils.cpu_affinity import set_cpu_affinity
|
from utils.cpu_affinity import set_cpu_affinity
|
||||||
from config import MAX_TRADES
|
from config import (
|
||||||
|
MAX_TRADES,
|
||||||
|
MAX_TRADING_BPS_THRESHOLD,
|
||||||
|
MIN_DELAY_BETWEEN_TRADES_SECONDS,
|
||||||
|
MAX_INVENTORY,
|
||||||
|
PROFIT_MARGIN,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
gc.disable()
|
gc.disable()
|
||||||
session = requests.Session()
|
|
||||||
requests.get = session.get
|
|
||||||
requests.post = session.post
|
|
||||||
requests.put = session.put
|
|
||||||
requests.patch = session.patch
|
|
||||||
requests.delete = session.delete
|
|
||||||
requests.head = session.head
|
|
||||||
requests.options = session.options
|
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
def main():
|
||||||
|
|
||||||
logger = setup_logging()
|
logger = setup_logging()
|
||||||
set_cpu_affinity()
|
set_cpu_affinity()
|
||||||
logger.info("Polymarket HFT Market Maker started")
|
logger.info("Polymarket HFT Market Maker started")
|
||||||
init_global_client()
|
init_global_client()
|
||||||
await asyncio.sleep(2)
|
time.sleep(2)
|
||||||
if not is_client_ready():
|
if not is_client_ready():
|
||||||
logger.error("ClobClient is not ready. Exiting.")
|
logger.error("ClobClient is not ready. Exiting.")
|
||||||
return
|
return
|
||||||
up_token, down_token, market_slug = await fetch_tokens()
|
up_token, down_token, market_slug = fetch_tokens()
|
||||||
book = OrderBook(up_token, down_token, market_slug)
|
book = OrderBook(up_token, down_token, market_slug)
|
||||||
await asyncio.create_task(cache_token_trading_infos(book))
|
|
||||||
book.start()
|
book.start()
|
||||||
|
|
||||||
await asyncio.sleep(5) # Allow some time for initial order book data
|
time.sleep(5) # Allow some time for initial order book data
|
||||||
|
|
||||||
market_data = book.get_current_market_data()
|
market_data = book.get_current_market_data()
|
||||||
|
|
||||||
@@ -52,7 +48,7 @@ async def main():
|
|||||||
down_bid_price = 1 - up_ask_price
|
down_bid_price = 1 - up_ask_price
|
||||||
|
|
||||||
print(
|
print(
|
||||||
f"Initial Prices - UP: {up_bid_price:.2f}/{up_ask_price:.2f} | DOWN: {down_bid_price:.2f}/{down_ask_price:.2f}",
|
f"Initial Prices - UP: {up_bid_price:.2f}/{up_ask_price:.2f} | DOWN: {down_bid_price:.2f}/{down_ask_price:.2f} | Inventory: {book.inventory} / {MAX_INVENTORY}",
|
||||||
flush=True,
|
flush=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -62,11 +58,11 @@ async def main():
|
|||||||
book.stop()
|
book.stop()
|
||||||
logger.info("Trading session ended. Starting new session.")
|
logger.info("Trading session ended. Starting new session.")
|
||||||
gc.collect()
|
gc.collect()
|
||||||
await asyncio.sleep(10)
|
time.sleep(10)
|
||||||
reset_trades()
|
reset_trades()
|
||||||
up_token, down_token, market_slug = await fetch_tokens()
|
up_token, down_token, market_slug = fetch_tokens()
|
||||||
book = OrderBook(up_token, down_token, market_slug)
|
book = OrderBook(up_token, down_token, market_slug)
|
||||||
asyncio.create_task(cache_token_trading_infos(book))
|
cache_token_trading_infos(book)
|
||||||
book.start()
|
book.start()
|
||||||
|
|
||||||
market_data = book.get_current_market_data()
|
market_data = book.get_current_market_data()
|
||||||
@@ -76,54 +72,63 @@ async def main():
|
|||||||
up_bid_price = market_data["best_bid_price"]
|
up_bid_price = market_data["best_bid_price"]
|
||||||
up_ask_price = market_data["best_ask_price"]
|
up_ask_price = market_data["best_ask_price"]
|
||||||
|
|
||||||
if not ((0.2 < up_ask_price < 0.35) or (0.65 < up_bid_price < 0.8)):
|
if not ((0.2 < up_ask_price < 0.35) or (0.65 < up_bid_price < 0.8)) or (
|
||||||
|
abs(market_data["micro_vs_mid_bps"]) > MAX_TRADING_BPS_THRESHOLD
|
||||||
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
down_ask_price = 1 - up_bid_price
|
down_ask_price = 1 - up_bid_price
|
||||||
down_bid_price = 1 - up_ask_price
|
down_bid_price = 1 - up_ask_price
|
||||||
|
|
||||||
up_trend = up_bid_price > down_bid_price
|
if (
|
||||||
|
(get_trades_count() < MAX_TRADES)
|
||||||
if get_trades_count() < MAX_TRADES:
|
and (get_period_elapsed_seconds() < 500)
|
||||||
|
and (book.inventory < MAX_INVENTORY)
|
||||||
|
):
|
||||||
trading_side = book.last_signal
|
trading_side = book.last_signal
|
||||||
|
|
||||||
if (trading_side == SIGNALES.UP) and not up_trend:
|
if trading_side == SIGNALES.UP:
|
||||||
await place_anchor_and_hedge(
|
order_ids = place_anchor_and_hedge(
|
||||||
up_token,
|
up_token,
|
||||||
down_token,
|
down_token,
|
||||||
"UP",
|
"UP",
|
||||||
up_bid_price,
|
round(up_bid_price, 2),
|
||||||
size=5,
|
size=5,
|
||||||
|
signed_orders_cache=book.signed_orders_cache,
|
||||||
)
|
)
|
||||||
current_trades = increment_trades()
|
current_trades = increment_trades()
|
||||||
logger.info(
|
book.update_signed_orders_cache(
|
||||||
f"Placed UP anchor and hedge orders. Total trades: {current_trades}"
|
[round(up_bid_price, 2), round(1 - up_bid_price - PROFIT_MARGIN, 2)]
|
||||||
)
|
)
|
||||||
|
logger.info(
|
||||||
|
f"Placed UP anchor and hedge orders. Total trades: {current_trades}, Order IDs: {order_ids}"
|
||||||
|
)
|
||||||
|
time.sleep(MIN_DELAY_BETWEEN_TRADES_SECONDS)
|
||||||
|
|
||||||
elif (trading_side == SIGNALES.DOWN) and up_trend:
|
elif trading_side == SIGNALES.DOWN:
|
||||||
await place_anchor_and_hedge(
|
order_ids = place_anchor_and_hedge(
|
||||||
up_token,
|
up_token,
|
||||||
down_token,
|
down_token,
|
||||||
"DOWN",
|
"DOWN",
|
||||||
down_bid_price,
|
round(down_bid_price, 2),
|
||||||
size=5,
|
size=5,
|
||||||
|
signed_orders_cache=book.signed_orders_cache,
|
||||||
)
|
)
|
||||||
current_trades = increment_trades()
|
current_trades = increment_trades()
|
||||||
logger.info(
|
book.update_signed_orders_cache(
|
||||||
f"Placed DOWN anchor and hedge orders. Total trades: {current_trades}"
|
[round(down_bid_price, 2), round(1 - down_bid_price - PROFIT_MARGIN, 2)]
|
||||||
)
|
)
|
||||||
|
logger.info(
|
||||||
|
f"Placed DOWN anchor and hedge orders. Total trades: {current_trades}, Order IDs: {order_ids}"
|
||||||
|
)
|
||||||
|
time.sleep(MIN_DELAY_BETWEEN_TRADES_SECONDS)
|
||||||
|
|
||||||
await asyncio.sleep(0.01)
|
time.sleep(0.01)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
try:
|
try:
|
||||||
if os.name == "nt":
|
main()
|
||||||
asyncio.run(main())
|
|
||||||
else:
|
|
||||||
import uvloop
|
|
||||||
|
|
||||||
uvloop.run(main())
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("\nMarket maker stopped by user")
|
print("\nMarket maker stopped by user")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -0,0 +1,215 @@
|
|||||||
|
import os
|
||||||
|
import time
|
||||||
|
import requests
|
||||||
|
import asyncio
|
||||||
|
from web3 import Web3
|
||||||
|
from eth_account import Account
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from web3.middleware import ExtraDataToPOAMiddleware
|
||||||
|
from abi.ctfAbi import ctf_abi
|
||||||
|
from abi.safeAbi import safe_abi
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
CONDITIONAL_TOKENS_FRAMEWORK_ADDRESS = "0x4D97DCd97eC945f40cF65F87097ACe5EA0476045"
|
||||||
|
NEG_RISK_ADAPTER_ADDRESS = "0xd91E80cF2E7be2e162c6513ceD06f1dD0dA35296"
|
||||||
|
USDC_ADDRESS = "0x2791bca1f2de4661ed88a30c99a7a9449aa84174"
|
||||||
|
USDCE_DIGITS = 6
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
|
||||||
|
def merge_tokens(condition_id, amount=None, neg_risk=False):
|
||||||
|
"""
|
||||||
|
Merge conditional tokens back to USDC.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
condition_id: The condition ID (bytes32 hex string)
|
||||||
|
amount: Amount to merge in USDC (e.g., "1.5"). If None, merges all available tokens
|
||||||
|
neg_risk: Whether to use NEG_RISK_ADAPTER (default: False)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if merge successful, False otherwise
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Connect to provider
|
||||||
|
rpc_url = os.getenv("RPC_URL")
|
||||||
|
w3 = Web3(Web3.HTTPProvider(rpc_url))
|
||||||
|
|
||||||
|
# Inject POA middleware for Polygon
|
||||||
|
from web3.middleware import ExtraDataToPOAMiddleware
|
||||||
|
|
||||||
|
w3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0)
|
||||||
|
|
||||||
|
# Load wallet and safe
|
||||||
|
private_key = os.getenv("PRIVATE_KEY")
|
||||||
|
account = Account.from_key(private_key)
|
||||||
|
safe_address = Web3.to_checksum_address(os.getenv("POLYMARKET_PROXY_ADDRESS"))
|
||||||
|
|
||||||
|
safe = w3.eth.contract(address=safe_address, abi=safe_abi)
|
||||||
|
|
||||||
|
# Determine merge amount
|
||||||
|
if amount is None:
|
||||||
|
# Get minimum balance of both positions
|
||||||
|
ctf_contract = w3.eth.contract(
|
||||||
|
address=Web3.to_checksum_address(CONDITIONAL_TOKENS_FRAMEWORK_ADDRESS),
|
||||||
|
abi=ctf_abi,
|
||||||
|
)
|
||||||
|
|
||||||
|
parent_collection_id = bytes(32)
|
||||||
|
collection_id_0 = ctf_contract.functions.getCollectionId(
|
||||||
|
parent_collection_id, bytes.fromhex(condition_id[2:]), 1
|
||||||
|
).call()
|
||||||
|
collection_id_1 = ctf_contract.functions.getCollectionId(
|
||||||
|
parent_collection_id, bytes.fromhex(condition_id[2:]), 2
|
||||||
|
).call()
|
||||||
|
|
||||||
|
position_id_0 = ctf_contract.functions.getPositionId(
|
||||||
|
Web3.to_checksum_address(USDC_ADDRESS), collection_id_0
|
||||||
|
).call()
|
||||||
|
position_id_1 = ctf_contract.functions.getPositionId(
|
||||||
|
Web3.to_checksum_address(USDC_ADDRESS), collection_id_1
|
||||||
|
).call()
|
||||||
|
|
||||||
|
balance_0 = ctf_contract.functions.balanceOf(
|
||||||
|
safe_address, position_id_0
|
||||||
|
).call()
|
||||||
|
balance_1 = ctf_contract.functions.balanceOf(
|
||||||
|
safe_address, position_id_1
|
||||||
|
).call()
|
||||||
|
|
||||||
|
amount_wei = min(balance_0, balance_1)
|
||||||
|
|
||||||
|
if amount_wei == 0:
|
||||||
|
print("Merge failed: No tokens to merge")
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
amount_wei = int(float(amount) * (10**USDCE_DIGITS))
|
||||||
|
|
||||||
|
# Encode merge transaction
|
||||||
|
ctf_contract = w3.eth.contract(abi=ctf_abi)
|
||||||
|
parent_collection_id = (
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
)
|
||||||
|
partition = [1, 2]
|
||||||
|
|
||||||
|
data = ctf_contract.functions.mergePositions(
|
||||||
|
Web3.to_checksum_address(USDC_ADDRESS),
|
||||||
|
bytes.fromhex(parent_collection_id[2:]),
|
||||||
|
bytes.fromhex(condition_id[2:]),
|
||||||
|
partition,
|
||||||
|
amount_wei,
|
||||||
|
)._encode_transaction_data()
|
||||||
|
|
||||||
|
# Sign and execute Safe transaction
|
||||||
|
nonce = safe.functions.nonce().call()
|
||||||
|
to = (
|
||||||
|
NEG_RISK_ADAPTER_ADDRESS
|
||||||
|
if neg_risk
|
||||||
|
else CONDITIONAL_TOKENS_FRAMEWORK_ADDRESS
|
||||||
|
)
|
||||||
|
|
||||||
|
tx_hash = safe.functions.getTransactionHash(
|
||||||
|
Web3.to_checksum_address(to),
|
||||||
|
0,
|
||||||
|
bytes.fromhex(data[2:]),
|
||||||
|
0, # operation: Call
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0, # safeTxGas, baseGas, gasPrice
|
||||||
|
"0x0000000000000000000000000000000000000000", # gasToken
|
||||||
|
"0x0000000000000000000000000000000000000000", # refundReceiver
|
||||||
|
nonce,
|
||||||
|
).call()
|
||||||
|
|
||||||
|
# Sign the hash
|
||||||
|
hash_bytes = Web3.to_bytes(
|
||||||
|
hexstr=tx_hash.hex() if hasattr(tx_hash, "hex") else tx_hash
|
||||||
|
)
|
||||||
|
signature_obj = account.unsafe_sign_hash(hash_bytes)
|
||||||
|
|
||||||
|
r = signature_obj.r.to_bytes(32, byteorder="big")
|
||||||
|
s = signature_obj.s.to_bytes(32, byteorder="big")
|
||||||
|
v = signature_obj.v.to_bytes(1, byteorder="big")
|
||||||
|
signature = r + s + v
|
||||||
|
|
||||||
|
# Build and send transaction
|
||||||
|
tx = safe.functions.execTransaction(
|
||||||
|
Web3.to_checksum_address(to),
|
||||||
|
0,
|
||||||
|
bytes.fromhex(data[2:]),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
"0x0000000000000000000000000000000000000000",
|
||||||
|
"0x0000000000000000000000000000000000000000",
|
||||||
|
signature,
|
||||||
|
).build_transaction(
|
||||||
|
{
|
||||||
|
"from": account.address,
|
||||||
|
"nonce": w3.eth.get_transaction_count(account.address),
|
||||||
|
"gas": 500000,
|
||||||
|
"gasPrice": w3.eth.gas_price,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
signed_tx = account.sign_transaction(tx)
|
||||||
|
tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction)
|
||||||
|
|
||||||
|
# Wait for receipt
|
||||||
|
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
|
||||||
|
|
||||||
|
# Check if transaction succeeded
|
||||||
|
if receipt["status"] == 1:
|
||||||
|
print(f"Merge successful! Amount: {amount_wei / 10**USDCE_DIGITS} USDC")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print("Merge failed: Transaction reverted")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Merge failed: {str(e)}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
private_key = os.getenv("PRIVATE_KEY")
|
||||||
|
account = Account.from_key(private_key)
|
||||||
|
safe_address = Web3.to_checksum_address(os.getenv("POLYMARKET_PROXY_ADDRESS"))
|
||||||
|
w3 = Web3(Web3.HTTPProvider(os.getenv("RPC_URL")))
|
||||||
|
w3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0)
|
||||||
|
matic_balance = w3.eth.get_balance(account.address)
|
||||||
|
proxy_balance = w3.eth.get_balance(safe_address)
|
||||||
|
|
||||||
|
print(f"--- Wallet Check ---")
|
||||||
|
print(f"Signer Address (from Private Key): {account.address}")
|
||||||
|
print(f"Signer POL Balance: {w3.from_wei(matic_balance, 'ether')} POL")
|
||||||
|
print(f"Proxy Address (Safe): {safe_address}")
|
||||||
|
print(f"Proxy POL Balance: {w3.from_wei(proxy_balance, 'ether')} POL")
|
||||||
|
print(f"--------------------")
|
||||||
|
|
||||||
|
if matic_balance == 0:
|
||||||
|
raise Exception(f"STOP: Your Signer address {account.address} has NO gas!")
|
||||||
|
|
||||||
|
|
||||||
|
async def do_it():
|
||||||
|
|
||||||
|
url = f"https://data-api.polymarket.com/positions?sizeThreshold=1&limit=100&sortBy=TOKENS&sortDirection=DESC&user={os.getenv('POLYMARKET_PROXY_ADDRESS')}&mergeable=true"
|
||||||
|
|
||||||
|
response = requests.get(url).json()
|
||||||
|
if not response:
|
||||||
|
print("No mergeable positions found.")
|
||||||
|
return
|
||||||
|
condition_ids = set([token["conditionId"] for token in response])
|
||||||
|
condition_ids
|
||||||
|
for condition_id in condition_ids:
|
||||||
|
print("Merging tokens for condition ID:", condition_id)
|
||||||
|
merge_tokens(condition_id)
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
while True:
|
||||||
|
asyncio.create_task(do_it())
|
||||||
|
await asyncio.sleep(10)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
import asyncio
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import websockets
|
|
||||||
from utils.clob_client import get_client
|
|
||||||
from config import POLYMARKET_WS_USER_URL
|
|
||||||
from in_memory_db.utils import add_item as in_memory_db_add_item
|
|
||||||
|
|
||||||
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.INFO,
|
|
||||||
format="%(asctime)s.%(msecs)03d [%(levelname)s] %(message)s",
|
|
||||||
datefmt="%H:%M:%S",
|
|
||||||
handlers=[logging.StreamHandler(sys.stdout)],
|
|
||||||
)
|
|
||||||
|
|
||||||
logger = logging.getLogger("OrderListener")
|
|
||||||
client = get_client()
|
|
||||||
creds = client.create_or_derive_api_creds()
|
|
||||||
|
|
||||||
|
|
||||||
async def handle_message(message):
|
|
||||||
try:
|
|
||||||
data = json.loads(message)
|
|
||||||
logger.debug(f"Received Message: {data}")
|
|
||||||
data_type = data.get("type")
|
|
||||||
if data_type in ["PLACEMENT", "CANCELLATION"]:
|
|
||||||
return
|
|
||||||
orders_ids = []
|
|
||||||
orders_ids.append(data.get("id"))
|
|
||||||
|
|
||||||
if data_type == "TRADE":
|
|
||||||
for orders in data.get("maker_orders", []):
|
|
||||||
orders_ids.append(orders.get("order_id"))
|
|
||||||
|
|
||||||
for order_id in orders_ids:
|
|
||||||
in_memory_db_add_item(order_id)
|
|
||||||
logger.info(f"Added Order ID: {order_id} into in-memory DB")
|
|
||||||
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
logger.error("Failed to decode message as JSON")
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
async def hedger_main():
|
|
||||||
logger.info("🔌 Connecting to WebSocket...")
|
|
||||||
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
async with websockets.connect(
|
|
||||||
POLYMARKET_WS_USER_URL,
|
|
||||||
ping_interval=10,
|
|
||||||
ping_timeout=10,
|
|
||||||
close_timeout=5,
|
|
||||||
) as ws:
|
|
||||||
auth_payload = {
|
|
||||||
"type": "user",
|
|
||||||
"auth": {
|
|
||||||
"apiKey": creds.api_key,
|
|
||||||
"secret": creds.api_secret,
|
|
||||||
"passphrase": creds.api_passphrase,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
await ws.send(json.dumps(auth_payload))
|
|
||||||
logger.info("🌐 WS Authenticated. Listening Orders...")
|
|
||||||
async for message in ws:
|
|
||||||
await handle_message(message)
|
|
||||||
|
|
||||||
except (websockets.ConnectionClosed, OSError, ConnectionRefusedError) as e:
|
|
||||||
logger.warning(f"🔌 Disconnected: {e}. Reconnecting...")
|
|
||||||
continue
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Unexpected Error: {e}", exc_info=True)
|
|
||||||
continue
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
try:
|
|
||||||
if os.name != "nt":
|
|
||||||
import uvloop
|
|
||||||
|
|
||||||
uvloop.run(hedger_main())
|
|
||||||
else:
|
|
||||||
asyncio.run(hedger_main())
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
logger.info("Order Listener Stopped by User")
|
|
||||||
except Exception as e:
|
|
||||||
logger.critical(f"Fatal Error: {e}", exc_info=True)
|
|
||||||
exit(1)
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
nohup in_memory_db/in_mem_db &
|
source ~/miniconda3/bin/activate
|
||||||
nohup python order_listener.py &
|
nohup python merger.py &
|
||||||
sudo env "PATH=$PATH" python main.py
|
sudo env "PATH=$PATH" python main.py
|
||||||
@@ -5,8 +5,3 @@ rm ~/miniconda3/miniconda.sh
|
|||||||
source ~/miniconda3/bin/activate
|
source ~/miniconda3/bin/activate
|
||||||
conda init --all
|
conda init --all
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
sudo apt update -y
|
|
||||||
sudo apt install build-essential curl -y
|
|
||||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
|
||||||
source $HOME/.cargo/env
|
|
||||||
rustc in_memory_db/in_mem_db.rs -o in_memory_db/in_mem_db
|
|
||||||
|
|||||||
+47
-27
@@ -1,18 +1,18 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from py_clob_client.clob_types import OrderArgs, OrderType
|
from py_clob_client.clob_types import OrderArgs, OrderType
|
||||||
from py_clob_client.order_builder.constants import BUY
|
from py_clob_client.order_builder.constants import BUY
|
||||||
from config import PROFIT_MARGIN, PLACE_OPPOSITE_ORDER
|
from config import PROFIT_MARGIN, PLACE_OPPOSITE_ORDER
|
||||||
from utils.clob_client import get_client
|
from utils.clob_client import get_client
|
||||||
from utils.trade_counter import decrement_trades
|
from utils.trade_counter import decrement_trades
|
||||||
from in_memory_db.utils import contains_item as in_memory_db_contains_item
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
async def cache_token_trading_infos(
|
def cache_token_trading_infos(
|
||||||
order_book,
|
order_book,
|
||||||
) -> None:
|
) -> None:
|
||||||
client = get_client()
|
client = get_client()
|
||||||
@@ -26,8 +26,8 @@ async def cache_token_trading_infos(
|
|||||||
client.get_fee_rate_bps(down_token_id)
|
client.get_fee_rate_bps(down_token_id)
|
||||||
|
|
||||||
|
|
||||||
async def place_anchor_and_hedge(
|
def place_anchor_and_hedge(
|
||||||
up_token_id, down_token_id, anchor_side, price, size=5
|
up_token_id, down_token_id, anchor_side, price, size=5, signed_orders_cache=None
|
||||||
):
|
):
|
||||||
if anchor_side == "UP":
|
if anchor_side == "UP":
|
||||||
anchor_token_id = up_token_id
|
anchor_token_id = up_token_id
|
||||||
@@ -36,35 +36,52 @@ async def place_anchor_and_hedge(
|
|||||||
anchor_token_id = down_token_id
|
anchor_token_id = down_token_id
|
||||||
hedge_token_id = up_token_id
|
hedge_token_id = up_token_id
|
||||||
|
|
||||||
asyncio.create_task(place_limit_order(anchor_token_id, price, size))
|
with ThreadPoolExecutor(max_workers=2) as executor:
|
||||||
asyncio.create_task(
|
future1 = executor.submit(
|
||||||
place_limit_order(hedge_token_id, 1 - price - PROFIT_MARGIN, size)
|
place_limit_order_sync,
|
||||||
)
|
anchor_token_id,
|
||||||
|
price,
|
||||||
|
size,
|
||||||
|
signed_orders_cache,
|
||||||
|
)
|
||||||
|
future2 = executor.submit(
|
||||||
|
place_limit_order_sync,
|
||||||
|
hedge_token_id,
|
||||||
|
round(1 - price - PROFIT_MARGIN, 2),
|
||||||
|
size,
|
||||||
|
signed_orders_cache,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Wait for both to complete
|
||||||
|
order_ids = [future1.result(), future2.result()]
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Placed anchor and hedge orders: Anchor Token ID={anchor_token_id}, Hedge Token ID={hedge_token_id}"
|
f"Placed anchor and hedge orders: Anchor Token ID={anchor_token_id}, Hedge Token ID={hedge_token_id}, Order IDs={order_ids}"
|
||||||
)
|
)
|
||||||
|
return order_ids
|
||||||
|
|
||||||
|
|
||||||
async def place_limit_order(token_id: str, price: float, size: int, expire=False):
|
def place_limit_order_sync(
|
||||||
|
token_id: str, price: float, size: int = 5, signed_orders_cache=None
|
||||||
|
) -> str:
|
||||||
|
"""Synchronous version of place_limit_order for use with ThreadPoolExecutor"""
|
||||||
client = get_client()
|
client = get_client()
|
||||||
expiration = 0
|
|
||||||
if expire:
|
|
||||||
one_minute = 60
|
|
||||||
desired_seconds = 5
|
|
||||||
expiration = int(time.time()) + one_minute + desired_seconds
|
|
||||||
try:
|
|
||||||
|
|
||||||
order_args = OrderArgs(
|
try:
|
||||||
token_id=token_id,
|
if signed_orders_cache and (token_id, price) in signed_orders_cache:
|
||||||
price=price,
|
signed_order = signed_orders_cache[(token_id, price)]
|
||||||
size=size,
|
logger.info(
|
||||||
side=BUY,
|
f"Using cached signed order for Token ID={token_id}, Price={price}"
|
||||||
expiration=expiration,
|
)
|
||||||
)
|
else:
|
||||||
signed_order = client.create_order(order_args)
|
order_args = OrderArgs(
|
||||||
response = client.post_order(
|
token_id=token_id,
|
||||||
signed_order, OrderType.GTD if expire else OrderType.GTC
|
price=price,
|
||||||
)
|
size=size,
|
||||||
|
side=BUY,
|
||||||
|
)
|
||||||
|
signed_order = client.create_order(order_args)
|
||||||
|
response = client.post_order(signed_order)
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Placed limit order: Token ID={token_id}, Price={price}, Size={size}, ID={response['orderID']}"
|
f"Placed limit order: Token ID={token_id}, Price={price}, Size={size}, ID={response['orderID']}"
|
||||||
)
|
)
|
||||||
@@ -72,3 +89,6 @@ async def place_limit_order(token_id: str, price: float, size: int, expire=False
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error placing order for token {token_id}: {e}")
|
logger.error(f"Error placing order for token {token_id}: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import requests
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def get_inventory(slug=None):
|
||||||
|
|
||||||
|
url = f"https://data-api.polymarket.com/positions?sizeThreshold=1&user={os.getenv('POLYMARKET_PROXY_ADDRESS')}&mergeable=false"
|
||||||
|
|
||||||
|
response = requests.get(url).json()
|
||||||
|
size = 0
|
||||||
|
for pos in response:
|
||||||
|
if pos and pos.get("slug") == slug:
|
||||||
|
size += pos.get("size")
|
||||||
|
return round(size / 5)
|
||||||
+73
-8
@@ -1,5 +1,4 @@
|
|||||||
import os
|
import os
|
||||||
import asyncio
|
|
||||||
import bisect
|
import bisect
|
||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
@@ -8,7 +7,10 @@ import threading
|
|||||||
import websocket
|
import websocket
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from config import POLYMARKET_WS_MARKET_URL, TRADING_BPS_THRESHOLD
|
from config import POLYMARKET_WS_MARKET_URL, TRADING_BPS_THRESHOLD
|
||||||
|
from py_clob_client import OrderArgs
|
||||||
|
from py_clob_client.order_builder.constants import BUY
|
||||||
from utils.clob_client import get_client
|
from utils.clob_client import get_client
|
||||||
|
from utils.inventory import get_inventory
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -34,6 +36,8 @@ class OrderBook:
|
|||||||
"order_book": {"bids": [], "asks": []},
|
"order_book": {"bids": [], "asks": []},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.signed_orders_cache = {}
|
||||||
|
|
||||||
self.ws = None
|
self.ws = None
|
||||||
self.running = False
|
self.running = False
|
||||||
self.thread = None
|
self.thread = None
|
||||||
@@ -43,6 +47,10 @@ class OrderBook:
|
|||||||
self.monitoring_running = False
|
self.monitoring_running = False
|
||||||
|
|
||||||
self.last_signal = SIGNALES.NEUTRAL
|
self.last_signal = SIGNALES.NEUTRAL
|
||||||
|
self.inventory = 0
|
||||||
|
self.inventory_thread = None
|
||||||
|
self.inventory_running = False
|
||||||
|
self.create_signed_orders_cache()
|
||||||
|
|
||||||
def _on_message(self, ws, message):
|
def _on_message(self, ws, message):
|
||||||
|
|
||||||
@@ -100,25 +108,46 @@ class OrderBook:
|
|||||||
|
|
||||||
self.running = True
|
self.running = True
|
||||||
self.monitoring_running = True
|
self.monitoring_running = True
|
||||||
|
self.inventory_running = True
|
||||||
|
|
||||||
self.thread = threading.Thread(target=self._connect, daemon=True)
|
self.thread = threading.Thread(target=self._connect, daemon=True)
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
|
|
||||||
self.monitoring_thread = threading.Thread(
|
self.monitoring_thread = threading.Thread(
|
||||||
target=lambda: asyncio.run(self._continuous_trading_monitor()), daemon=True
|
target=self._continuous_trading_monitor, daemon=True
|
||||||
)
|
)
|
||||||
self.monitoring_thread.start()
|
self.monitoring_thread.start()
|
||||||
|
|
||||||
logger.info("WebSocket price stream and trading monitor started")
|
self.inventory_thread = threading.Thread(
|
||||||
|
target=self._inventory_updater, daemon=True
|
||||||
|
)
|
||||||
|
self.inventory_thread.start()
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"WebSocket price stream, trading monitor, and inventory updater started"
|
||||||
|
)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.running = False
|
self.running = False
|
||||||
self.monitoring_running = False
|
self.monitoring_running = False
|
||||||
|
self.inventory_running = False
|
||||||
|
|
||||||
if self.ws:
|
if self.ws:
|
||||||
self.ws.close()
|
self.ws.close()
|
||||||
|
|
||||||
logger.info("🛑 WebSocket price stream and trading monitor stopped")
|
logger.info(
|
||||||
|
"🛑 WebSocket price stream, trading monitor, and inventory updater stopped"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _inventory_updater(self):
|
||||||
|
logger.info("Started inventory updater thread")
|
||||||
|
while self.inventory_running:
|
||||||
|
try:
|
||||||
|
self.inventory = get_inventory(self.slug)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error updating inventory: {e}")
|
||||||
|
time.sleep(1)
|
||||||
|
logger.info("Stopped inventory updater thread")
|
||||||
|
|
||||||
def is_connected(self):
|
def is_connected(self):
|
||||||
with self.lock:
|
with self.lock:
|
||||||
@@ -177,14 +206,14 @@ class OrderBook:
|
|||||||
"asks": asks,
|
"asks": asks,
|
||||||
}
|
}
|
||||||
|
|
||||||
async def _continuous_trading_monitor(self):
|
def _continuous_trading_monitor(self):
|
||||||
logger.info("Started continuous trading monitor")
|
logger.info("Started continuous trading monitor")
|
||||||
|
|
||||||
while self.monitoring_running:
|
while self.monitoring_running:
|
||||||
try:
|
try:
|
||||||
market_data = self.get_current_market_data()
|
market_data = self.get_current_market_data()
|
||||||
if not market_data:
|
if not market_data:
|
||||||
await asyncio.sleep(0.1)
|
time.sleep(0.1)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
micro_vs_mid_bps = market_data["micro_vs_mid_bps"]
|
micro_vs_mid_bps = market_data["micro_vs_mid_bps"]
|
||||||
@@ -200,14 +229,50 @@ class OrderBook:
|
|||||||
if current_signal and current_signal != self.last_signal:
|
if current_signal and current_signal != self.last_signal:
|
||||||
self.last_signal = current_signal
|
self.last_signal = current_signal
|
||||||
|
|
||||||
await asyncio.sleep(0.005)
|
time.sleep(0.005)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error in continuous trading monitor: {e}")
|
logger.error(f"Error in continuous trading monitor: {e}")
|
||||||
await asyncio.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
logger.info("Stopped continuous trading monitor")
|
logger.info("Stopped continuous trading monitor")
|
||||||
|
|
||||||
|
def create_signed_orders_cache(self):
|
||||||
|
start = time.time()
|
||||||
|
prices = [0.01]
|
||||||
|
while prices[-1] < 0.99:
|
||||||
|
prices.append(round(prices[-1] + 0.01, 2))
|
||||||
|
|
||||||
|
client = get_client()
|
||||||
|
for price in prices:
|
||||||
|
for token_id in [self.up_token_id, self.down_token_id]:
|
||||||
|
order_args = OrderArgs(
|
||||||
|
token_id=token_id,
|
||||||
|
price=price,
|
||||||
|
size=5,
|
||||||
|
side=BUY,
|
||||||
|
)
|
||||||
|
signed_order = client.create_order(order_args)
|
||||||
|
self.signed_orders_cache[(token_id, price)] = signed_order
|
||||||
|
end = time.time()
|
||||||
|
logger.info(
|
||||||
|
f"Pre-created signed orders cache for tokens in {round((end - start) * 1000)} milliseconds"
|
||||||
|
)
|
||||||
|
|
||||||
|
def update_signed_orders_cache(self, prices):
|
||||||
|
client = get_client()
|
||||||
|
for price in prices:
|
||||||
|
for token_id in [self.up_token_id, self.down_token_id]:
|
||||||
|
order_args = OrderArgs(
|
||||||
|
token_id=token_id,
|
||||||
|
price=price,
|
||||||
|
size=5,
|
||||||
|
side=BUY,
|
||||||
|
)
|
||||||
|
signed_order = client.create_order(order_args)
|
||||||
|
self.signed_orders_cache[(token_id, price)] = signed_order
|
||||||
|
logger.info(f"Updated signed orders cache for new prices: {prices}")
|
||||||
|
|
||||||
def clear_screen(self):
|
def clear_screen(self):
|
||||||
os.system("cls" if os.name == "nt" else "clear")
|
os.system("cls" if os.name == "nt" else "clear")
|
||||||
|
|
||||||
|
|||||||
+9
-12
@@ -2,7 +2,7 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
from multiprocessing.util import get_logger
|
from multiprocessing.util import get_logger
|
||||||
from typing import Optional, Tuple
|
from typing import Optional, Tuple
|
||||||
import aiohttp
|
import requests
|
||||||
from .slug import get_market_slug
|
from .slug import get_market_slug
|
||||||
from config import GAMMA_API_URL, REQUEST_TIMEOUT
|
from config import GAMMA_API_URL, REQUEST_TIMEOUT
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ from config import GAMMA_API_URL, REQUEST_TIMEOUT
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
async def fetch_tokens(
|
def fetch_tokens(
|
||||||
coin: str = "btc",
|
coin: str = "btc",
|
||||||
) -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
) -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
||||||
|
|
||||||
@@ -21,17 +21,14 @@ async def fetch_tokens(
|
|||||||
slug = get_market_slug(coin)
|
slug = get_market_slug(coin)
|
||||||
url = f"{GAMMA_API_URL}/events/slug/{slug}"
|
url = f"{GAMMA_API_URL}/events/slug/{slug}"
|
||||||
|
|
||||||
async with aiohttp.ClientSession() as session:
|
response = requests.get(url, timeout=REQUEST_TIMEOUT)
|
||||||
async with session.get(
|
if response.status_code == 200:
|
||||||
url, timeout=aiohttp.ClientTimeout(total=REQUEST_TIMEOUT)
|
data = response.json()
|
||||||
) as response:
|
return _extract_tokens(data, slug)
|
||||||
if response.status == 200:
|
else:
|
||||||
data = await response.json()
|
logger.warning(f"API request failed with status {response.status_code}")
|
||||||
return _extract_tokens(data, slug)
|
|
||||||
else:
|
|
||||||
logger.warning(f"API request failed with status {response.status}")
|
|
||||||
|
|
||||||
except aiohttp.ClientError as e:
|
except requests.exceptions.RequestException as e:
|
||||||
logger.error(f"Network error fetching tokens: {e}")
|
logger.error(f"Network error fetching tokens: {e}")
|
||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
logger.error(f"Invalid JSON response: {e}")
|
logger.error(f"Invalid JSON response: {e}")
|
||||||
|
|||||||
Reference in New Issue
Block a user